@topconsultnpm/sdkui-react 6.20.0-t2 → 6.20.0-t3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/lib/components/base/TMPopUp.js +4 -0
  2. package/lib/components/base/TMTreeView.js +12 -8
  3. package/lib/components/choosers/TMDataListItemChooser.js +1 -1
  4. package/lib/components/choosers/TMDataListItemFields.js +1 -1
  5. package/lib/components/choosers/TMDataListItemPicker.d.ts +1 -0
  6. package/lib/components/choosers/TMDataListItemPicker.js +5 -1
  7. package/lib/components/choosers/TMUserChooser.js +1 -1
  8. package/lib/components/editors/TMMetadataValues.js +200 -40
  9. package/lib/components/editors/TMTextArea.d.ts +1 -0
  10. package/lib/components/editors/TMTextArea.js +6 -6
  11. package/lib/components/features/archive/TMArchive.d.ts +1 -0
  12. package/lib/components/features/archive/TMArchive.js +2 -2
  13. package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
  14. package/lib/components/features/documents/TMDcmtForm.js +16 -7
  15. package/lib/components/features/documents/TMFileUploader.d.ts +2 -0
  16. package/lib/components/features/documents/TMFileUploader.js +16 -8
  17. package/lib/components/features/documents/TMMasterDetailDcmts.js +25 -63
  18. package/lib/components/features/documents/TMRelationViewer.js +109 -40
  19. package/lib/components/features/search/TMSearchQueryPanel.js +3 -3
  20. package/lib/components/features/search/TMSearchResult.js +5 -13
  21. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +1 -1
  22. package/lib/components/features/search/TMSearchResultsMenuItems.js +4 -16
  23. package/lib/components/features/search/TMSignatureInfoContent.js +10 -6
  24. package/lib/components/features/search/TMTreeSelector.js +1 -1
  25. package/lib/components/features/tasks/TMTaskFormUtils.js +1 -1
  26. package/lib/components/features/workflow/TMWorkflowPopup.js +9 -19
  27. package/lib/components/features/workflow/diagram/DiagramItemForm.d.ts +2 -0
  28. package/lib/components/features/workflow/diagram/DiagramItemForm.js +32 -25
  29. package/lib/components/features/workflow/diagram/RecipientList.d.ts +3 -1
  30. package/lib/components/features/workflow/diagram/RecipientList.js +13 -9
  31. package/lib/components/features/workflow/diagram/WFDiagram.js +29 -2
  32. package/lib/components/features/workflow/diagram/workflowHelpers.js +31 -19
  33. package/lib/components/grids/TMRecentsManager.js +1 -1
  34. package/lib/components/viewers/TMMidViewer.js +2 -1
  35. package/lib/components/viewers/TMTidViewer.js +2 -1
  36. package/lib/helper/SDKUI_Globals.d.ts +4 -0
  37. package/lib/helper/SDKUI_Globals.js +9 -1
  38. package/lib/helper/SDKUI_Localizator.d.ts +12 -4
  39. package/lib/helper/SDKUI_Localizator.js +104 -24
  40. package/lib/helper/TMUtils.d.ts +9 -41
  41. package/lib/helper/TMUtils.js +79 -167
  42. package/lib/hooks/useDataUserIdItem.js +2 -2
  43. package/package.json +2 -2
  44. package/lib/components/features/search/TMSignSettingsForm.d.ts +0 -9
  45. package/lib/components/features/search/TMSignSettingsForm.js +0 -621
@@ -1,7 +1,7 @@
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
3
  import TMDcmtPreview from './TMDcmtPreview';
4
- import { AccessLevels, AppModules, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataTypes, ObjectClasses, ResultTypes, SDK_Globals, SDK_Localizator, SystemMIDsAsNumber, SystemTIDs, Task_States, TID_DID, UpdateEngineByID, UserListCacheService, ValidationItem, WorkflowCacheService, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
4
+ import { AccessLevels, AccessLevelsEx, AppModules, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataDomains, MetadataDataTypes, ObjectClasses, ResultTypes, SDK_Globals, SDK_Localizator, SystemMIDsAsNumber, SystemTIDs, Task_States, TID_DID, UpdateEngineByID, UserListCacheService, ValidationItem, WorkflowCacheService, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
5
5
  import { WorkFlowApproveRejectPopUp, WorkFlowMoreInfoPopUp, WorkFlowOperationButtons, WorkFlowReAssignPopUp } from '../workflow/TMWorkflowPopup';
6
6
  import { DownloadTypes, FormModes, DcmtOperationTypes } from '../../../ts';
7
7
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
@@ -65,7 +65,7 @@ export var InvocationContext;
65
65
  let abortControllerLocal = new AbortController();
66
66
  ;
67
67
  //#endregion
68
- const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, showBackButton = true, onClose, onSavedAsyncCallback, TID, DID, formMode = FormModes.Update, canNext, canPrev, count, itemIndex, onNext, onPrev, allowNavigation = true, allowRelations = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, showDcmtFormSidebar = true, invocationContext = InvocationContext.Default, titleModal, isModal = false, widthModal = "100%", heightModal = "100%", groupId, onWFOperationCompleted, onTaskCompleted, onTaskCreateRequest, inputFile = null, taskFormDialogComponent, moreInfoTasks, connectorFileSave = undefined, inputMids = [], openS4TViewer = false, onOpenS4TViewerRequest, s4TViewerDialogComponent, enableDragDropOverlay = false, openPdfEditor, passToSearch, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowButtonsRefs = false, onReferenceClick, }) => {
68
+ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, showBackButton = true, onClose, onSavedAsyncCallback, TID, DID, formMode = FormModes.Update, canNext, canPrev, count, itemIndex, onNext, onPrev, allowNavigation = true, allowRelations = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, showDcmtFormSidebar = true, invocationContext = InvocationContext.Default, titleModal, isModal = false, widthModal = "100%", heightModal = "100%", groupId, onWFOperationCompleted, onTaskCompleted, onTaskCreateRequest, inputFile = null, taskFormDialogComponent, moreInfoTasks, connectorFileSave = undefined, inputMids = [], openS4TViewer = false, onOpenS4TViewerRequest, s4TViewerDialogComponent, enableDragDropOverlay = false, onScanRequest, openPdfEditor, passToSearch, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowButtonsRefs = false, onReferenceClick, }) => {
69
69
  const { showHistory, showHistoryCallback, hideHistoryCallback, showCheckoutInformationForm, commentFormState, hideCommentFormCallback, showCheckoutInformationFormCallback, hideCheckoutInformationFormCallback, copyCheckoutPathToClipboardCallback, handleCheckOutCallback, handleCheckInCallback, refreshPreviewTrigger, showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, } = useCheckInOutOperations();
70
70
  const [id, setID] = useState('');
71
71
  const [showWaitPanelLocal, setShowWaitPanelLocal] = useState(false);
@@ -633,11 +633,15 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
633
633
  const handlePassToSearch = useCallback(() => {
634
634
  if (!passToSearch)
635
635
  return;
636
+ if (fromDTD?.perm?.canSearch !== AccessLevelsEx.Yes && fromDTD?.perm?.canSearch !== AccessLevelsEx.Mixed) {
637
+ ShowAlert({ title: SDKUI_Localizator.PassToSearch, message: 'Non hai i permessi per effettuare la ricerca.', mode: 'warning', duration: 3000 });
638
+ return;
639
+ }
636
640
  const outputMids = formData
637
641
  .filter(md => md.mid && md.mid > 100 && md.value && md.value.length > 0)
638
642
  .map(md => ({ mid: md.mid, value: md.value }));
639
643
  passToSearch(outputMids, TID);
640
- }, [passToSearch, formData, TID]);
644
+ }, [passToSearch, formData, TID, fromDTD?.perm?.canSearch]);
641
645
  const isPreviewDisabled = useMemo(() => layoutMode === LayoutModes.Ark && fromDTD?.archiveConstraint === ArchiveConstraints.OnlyMetadata, [layoutMode, fromDTD?.archiveConstraint]);
642
646
  const isBoardDisabled = useMemo(() => layoutMode !== LayoutModes.Update || fromDTD?.hasBlog !== 1, [layoutMode, fromDTD?.hasBlog]);
643
647
  const isSysMetadataDisabled = useMemo(() => layoutMode !== LayoutModes.Update, [layoutMode]);
@@ -1275,7 +1279,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
1275
1279
  ]);
1276
1280
  const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: TID, did: DID, allTasks: allTasks, fetchBlogDataTrigger: refreshBlogTrigger, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), [TID, DID, allTasks, refreshBlogTrigger, handleNavigateToWGs, handleNavigateToDossiers]);
1277
1281
  const tmSysMetadata = useMemo(() => _jsx(TMMetadataValues, { layoutMode: layoutMode, openChooserBySingleClick: !isOpenDistinctValues, TID: TID, isReadOnly: true, deviceType: deviceType, metadataValues: formData.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: formData.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [], inputMids: inputMids }), [TID, layoutMode, formData, deviceType, inputMids]);
1278
- const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { currentDcmt: currentDcmt, dcmtFile: dcmtFile ?? inputFile, deviceType: deviceType, fromDTD: fromDTD, layoutMode: layoutMode, onFileUpload: (file) => { setDcmtFile(file); }, openPdfEditor: openPdfEditor, enableDragDropOverlay: enableDragDropOverlay }, refreshPreviewTrigger), [currentDcmt, dcmtFile, deviceType, fromDTD, layoutMode, inputFile, enableDragDropOverlay, setDcmtFile, openPdfEditor]);
1282
+ const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { currentDcmt: currentDcmt, dcmtFile: dcmtFile ?? inputFile, deviceType: deviceType, fromDTD: fromDTD, layoutMode: layoutMode, onFileUpload: (file) => { setDcmtFile(file); }, openPdfEditor: openPdfEditor, enableDragDropOverlay: enableDragDropOverlay, onScanRequest: onScanRequest }, refreshPreviewTrigger), [currentDcmt, dcmtFile, deviceType, fromDTD, layoutMode, inputFile, enableDragDropOverlay, setDcmtFile, onScanRequest, openPdfEditor]);
1279
1283
  const tmWF = useMemo(() => {
1280
1284
  if (isWFDataLoading) {
1281
1285
  return (_jsx("div", { style: {
@@ -1745,7 +1749,12 @@ export const validateMetadataList = (mvdList = []) => {
1745
1749
  return validationItems;
1746
1750
  }, []);
1747
1751
  };
1748
- const isValidForValidation = (mvd) => { return mvd.mid > 99; };
1752
+ const isValidForValidation = (mvd) => {
1753
+ // I campi Computed non vanno validati (sono calcolati automaticamente)
1754
+ if (mvd.md?.dataDomain === MetadataDataDomains.Computed)
1755
+ return false;
1756
+ return mvd.mid > 99;
1757
+ };
1749
1758
  const validateRequiredField = (mvd, value, validationItems) => {
1750
1759
  if (mvd.isRequired === '1' && !value.trim()) {
1751
1760
  const message = SDKUI_Localizator.RequiredField;
@@ -1772,12 +1781,12 @@ const WfButtonStateHandler = ({ isWFDisabled }) => {
1772
1781
  }, [isWFDisabled, setToolbarButtonDisabled]);
1773
1782
  return null; // Non renderizza elementi nel DOM
1774
1783
  };
1775
- const TMDcmtPreviewWrapper = ({ currentDcmt, layoutMode, fromDTD, dcmtFile, deviceType, isVisible, onFileUpload, openPdfEditor, enableDragDropOverlay = false }) => {
1784
+ const TMDcmtPreviewWrapper = ({ currentDcmt, layoutMode, fromDTD, dcmtFile, deviceType, isVisible, onFileUpload, openPdfEditor, enableDragDropOverlay = false, onScanRequest }) => {
1776
1785
  const { setPanelVisibilityById, toggleMaximize, isResizingActive, countVisibleLeafPanels, panelVisibility } = useTMPanelManagerContext();
1777
1786
  const isMobile = deviceType === DeviceType.MOBILE;
1778
1787
  return (layoutMode === LayoutModes.Update ?
1779
1788
  _jsx(TMDcmtPreview, { isVisible: isVisible, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, dcmtData: currentDcmt, isResizingActive: isResizingActive }) :
1780
- _jsx(TMFileUploader, { fromDTD: fromDTD, onFileUpload: onFileUpload, openPdfEditor: openPdfEditor, onClose: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, isRequired: fromDTD?.archiveConstraint === ArchiveConstraints.ContentCompulsory && dcmtFile === null, defaultBlob: dcmtFile, deviceType: deviceType, isResizingActive: isResizingActive, enableDragDropOverlay: panelVisibility['tmDcmtPreview'] && enableDragDropOverlay }));
1789
+ _jsx(TMFileUploader, { fromDTD: fromDTD, onFileUpload: onFileUpload, openPdfEditor: openPdfEditor, onClose: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, isRequired: fromDTD?.archiveConstraint === ArchiveConstraints.ContentCompulsory && dcmtFile === null, defaultBlob: dcmtFile, deviceType: deviceType, isResizingActive: isResizingActive, enableDragDropOverlay: panelVisibility['tmDcmtPreview'] && enableDragDropOverlay, onScanRequest: onScanRequest }));
1781
1790
  };
1782
1791
  const Ribbon = styled.div `
1783
1792
  font-size: 0.85rem;
@@ -6,12 +6,14 @@ interface ITMFileUploader {
6
6
  onFileUpload?: (file: File | null) => void;
7
7
  openPdfEditor?: (fromDTD?: DcmtTypeDescriptor, file?: File | null, handleFile?: (file: File) => void) => void;
8
8
  onClose?: () => void;
9
+ onScanRequest?: (onFileScanned: (file: File) => void) => void;
9
10
  isRequired?: boolean;
10
11
  defaultBlob?: File | null;
11
12
  deviceType?: DeviceType;
12
13
  isResizingActive?: boolean;
13
14
  showTMPanel?: boolean;
14
15
  enableDragDropOverlay?: boolean;
16
+ showScannerIcon?: boolean;
15
17
  }
16
18
  declare const TMFileUploader: React.FC<ITMFileUploader>;
17
19
  export default TMFileUploader;
@@ -1,7 +1,7 @@
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
3
  import styled from 'styled-components';
4
- import { IconFolderOpen, IconScanner, SDKUI_Localizator, formatBytes, IconClear, extensionHandler, IconCloseOutline, IconMenuVertical, TMCommandsContextMenu, IconEdit } from '../../../helper';
4
+ import { IconFolderOpen, IconScanner, SDKUI_Localizator, formatBytes, IconClear, extensionHandler, IconCloseOutline, IconMenuVertical, TMCommandsContextMenu, IconEdit, isPdfEditorAvailable, SDKUI_Globals } from '../../../helper';
5
5
  import { useBetaFeatures } from '../../../hooks/useBetaFeatures';
6
6
  import usePreventFileDrop from '../../../hooks/usePreventFileDrop';
7
7
  import { FileExtensionHandler } from '../../../ts';
@@ -13,8 +13,17 @@ import TMTooltip from '../../base/TMTooltip';
13
13
  import { TMFileViewer, StyledHeaderIcon } from './TMDcmtPreview';
14
14
  import TMPanel from '../../base/TMPanel';
15
15
  import TMDragDropOverlay from './TMDragDropOverlay';
16
- import { LicenseModuleStatus, SDK_Globals } from '@topconsultnpm/sdk-ts';
17
- const TMFileUploader = ({ fromDTD, deviceType = DeviceType.DESKTOP, onClose, onFileUpload, openPdfEditor, isRequired = false, defaultBlob = null, isResizingActive, showTMPanel = true, enableDragDropOverlay = false }) => {
16
+ // Helper per verificare se la licenza scanner è disponibile
17
+ const isScannerLicenseConfigured = () => {
18
+ try {
19
+ const scannerLicense = SDKUI_Globals.userSettings.advancedSettings.scannerLicense;
20
+ return scannerLicense && scannerLicense.trim() !== '';
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ };
26
+ const TMFileUploader = ({ fromDTD, deviceType = DeviceType.DESKTOP, onClose, onFileUpload, openPdfEditor, onScanRequest, isRequired = false, defaultBlob = null, isResizingActive, showTMPanel = true, enableDragDropOverlay = false, showScannerIcon = true }) => {
18
27
  const isBetaFeaturesEnabled = useBetaFeatures();
19
28
  const [dragOver, setDragOver] = useState(false);
20
29
  const [uploadedFile, setUploadedFile] = useState(defaultBlob);
@@ -87,20 +96,19 @@ const TMFileUploader = ({ fromDTD, deviceType = DeviceType.DESKTOP, onClose, onF
87
96
  document.getElementById('fileInput')?.click();
88
97
  }, []);
89
98
  let content = !uploadedFile ?
90
- _jsxs("div", { style: { display: 'flex', gap: 10, width: '100%', height: '100%' }, children: [_jsx(HiddenInput, { id: "fileInput", type: "file", onChange: handleInputChange }), _jsxs(UploadContainer, { ref: uploaderRef, tabIndex: 0, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, style: { backgroundColor: dragOver ? '#76b1e6' : 'white' }, onDoubleClick: browseHandler, "$isRequired": isRequired, children: [_jsxs("div", { style: { display: 'flex', gap: '10px', flexDirection: 'column', position: 'absolute', right: 5, top: 5 }, children: [_jsx(TMButton, { btnStyle: 'icon', caption: 'Sfoglia', color: isRequired && !uploadedFile ? 'error' : 'primary', onClick: browseHandler, icon: _jsx(IconFolderOpen, { fontSize: 22 }) }), isBetaFeaturesEnabled && _jsx(TMButton, { btnStyle: 'icon', caption: 'Scanner', color: 'primary', onClick: () => { ShowAlert({ message: 'Funzionalità scanner in fase di sviluppo.', mode: 'info', duration: 3000, title: 'Work in progress' }); }, icon: _jsx(IconScanner, { fontSize: 22 }) })] }), _jsx("p", { style: { fontSize: '1.2rem', fontWeight: 'bold' }, children: deviceType === DeviceType.MOBILE ? 'Clicca per sfogliare il tuo file' : 'Trascina il tuo file o fai doppio click per sfogliarlo' }), isRequired && _jsxs("p", { style: { fontWeight: 'bold' }, children: [" ", SDKUI_Localizator.RequiredField, " "] })] })] }) :
99
+ _jsxs("div", { style: { display: 'flex', gap: 10, width: '100%', height: '100%' }, children: [_jsx(HiddenInput, { id: "fileInput", type: "file", onChange: handleInputChange }), _jsxs(UploadContainer, { ref: uploaderRef, tabIndex: 0, onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, style: { backgroundColor: dragOver ? '#76b1e6' : 'white' }, onDoubleClick: browseHandler, "$isRequired": isRequired, children: [_jsxs("div", { style: { display: 'flex', gap: '10px', flexDirection: 'column', position: 'absolute', right: 5, top: 5 }, children: [_jsx(TMButton, { btnStyle: 'icon', caption: 'Sfoglia', color: isRequired && !uploadedFile ? 'error' : 'primary', onClick: browseHandler, icon: _jsx(IconFolderOpen, { fontSize: 22 }) }), isBetaFeaturesEnabled && showScannerIcon && isScannerLicenseConfigured() && onScanRequest && _jsx(TMButton, { btnStyle: 'icon', caption: 'Scanner', color: 'primary', onClick: () => { onScanRequest((file) => { onFileUpload?.(file); }); }, icon: _jsx(IconScanner, { fontSize: 22 }) }), isBetaFeaturesEnabled && showScannerIcon && isScannerLicenseConfigured() && !onScanRequest && _jsx(TMButton, { btnStyle: 'icon', caption: 'Scanner', color: 'primary', onClick: () => { ShowAlert({ message: 'Funzionalità scanner non disponibile in questo contesto.', mode: 'info', duration: 3000, title: 'Scanner' }); }, icon: _jsx(IconScanner, { fontSize: 22 }) })] }), _jsx("p", { style: { fontSize: '1.2rem', fontWeight: 'bold' }, children: deviceType === DeviceType.MOBILE ? 'Clicca per sfogliare il tuo file' : 'Trascina il tuo file o fai doppio click per sfogliarlo' }), isRequired && _jsxs("p", { style: { fontWeight: 'bold' }, children: [" ", SDKUI_Localizator.RequiredField, " "] })] })] }) :
91
100
  _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 10, width: '100%', height: '100%' }, children: [_jsxs("div", { style: { backgroundColor: 'white', padding: '5px 10px', borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'space-between', color: TMColors.primaryColor }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 5 }, children: [_jsx("p", { children: "File name:" }), _jsxs("div", { style: { fontWeight: 'bold' }, children: [fileName, " ", _jsxs("span", { children: [" ", ` (${formatBytes(fileSize)})`, " "] })] })] }), uploadedFile && _jsx(TMButton, { btnStyle: 'icon', color: 'error', caption: 'Pulisci', onClick: () => clearFile(true), icon: _jsx(IconClear, { fontSize: 22 }) })] }), extensionHandler(fileExt) === FileExtensionHandler.READY_TO_SHOW ? _jsx(TMFileViewer, { fileBlob: uploadedFile, isResizingActive: isResizingActive }) :
92
101
  _jsx("div", { style: { backgroundColor: '#f6dbdb', padding: '5px 10px', borderRadius: 8, display: 'flex', alignItems: 'center', justifyContent: 'space-between', color: TMColors.error }, children: _jsxs("div", { children: [" ", 'Anteprima non disponibile.', fileExt && _jsx("b", { children: ` (*.${fileExt})` })] }) })] });
93
102
  const innerContent = (_jsxs("div", { style: { width: '100%', height: '100%', padding: '2px', display: 'flex', flexDirection: 'column', gap: 10 }, children: [enableDragDropOverlay && _jsx(TMDragDropOverlay, { handleFile: handleFile, refocusAfterFileInput: refocusAfterFileInput }), content] }));
94
103
  const toolbar = useMemo(() => {
95
- const isPdfEditorLicensed = SDK_Globals?.license?.dcmtArchiveLicenses?.[0]?.siX_60007?.status === LicenseModuleStatus.Licensed;
96
- return (_jsxs(_Fragment, { children: [(isPdfEditorLicensed && fileExt && fileExt.toLowerCase() === 'pdf') && (_jsx(TMCommandsContextMenu, { target: "#TMPanel-FileUploader-Commands-Header", menuItems: [
104
+ return (_jsxs(_Fragment, { children: [(isPdfEditorAvailable(fromDTD, fileExt) && openPdfEditor) && (_jsx(TMCommandsContextMenu, { target: "#TMPanel-FileUploader-Commands-Header", menuItems: [
97
105
  {
98
106
  icon: _jsx(IconEdit, {}),
99
107
  text: 'PDF Editor',
100
- onClick: () => openPdfEditor?.(fromDTD, uploadedFile, handleFile)
108
+ onClick: () => openPdfEditor(fromDTD, uploadedFile, handleFile)
101
109
  }
102
110
  ], showEvent: "click", children: _jsx(IconMenuVertical, { id: "TMPanel-FileUploader-Commands-Header", color: "white", cursor: "pointer" }) })), deviceType !== DeviceType.MOBILE && (_jsx(StyledHeaderIcon, { onClick: onClose, "$color": "white", children: _jsx(TMTooltip, { content: SDKUI_Localizator.Close, children: _jsx(IconCloseOutline, {}) }) }))] }));
103
- }, [deviceType, onClose]);
111
+ }, [deviceType, fromDTD, onClose]);
104
112
  return showTMPanel ? (_jsx(TMPanel, { ref: fileUploaderPanelRef, panelID: 'file-uploader-panel', title: SDKUI_Localizator.FileUpload, onBack: deviceType === DeviceType.MOBILE ? () => onClose?.() : undefined, toolbar: toolbar, children: innerContent })) : (innerContent);
105
113
  };
106
114
  const UploadContainer = styled.div `
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { DcmtTypeListCacheService, SDK_Localizator } from '@topconsultnpm/sdk-ts';
4
- import styled from 'styled-components';
5
4
  import TMRelationViewer from './TMRelationViewer';
6
5
  import TMContextMenu from '../../NewComponents/ContextMenu/TMContextMenu';
7
- import { genUniqueId, IconMultipleSelection, IconCheckFile, IconDetailDcmts, SDKUI_Localizator, IconMenuVertical, IconDataList, IconPreview, IconSearchCheck, IconBoard, IconDcmtTypeSys, IconShow, getMoreInfoTasksForDocument } from '../../../helper';
6
+ import { IconMultipleSelection, IconCheckFile, IconDetailDcmts, SDKUI_Localizator, IconMenuVertical, IconDataList, IconPreview, IconSearchCheck, IconBoard, IconDcmtTypeSys, IconShow, getMoreInfoTasksForDocument } from '../../../helper';
8
7
  import { FormModes, SearchResultContext } from '../../../ts';
9
8
  import { TMColors } from '../../../utils/theme';
9
+ import ShowAlert from '../../base/TMAlert';
10
10
  import { DeviceType } from '../../base/TMDeviceProvider';
11
11
  import { TMSaveFormButtonPrevious, TMSaveFormButtonNext } from '../../forms/TMSaveForm';
12
12
  import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
@@ -14,56 +14,15 @@ import { TMPanelManagerProvider, useTMPanelManagerContext } from '../../layout/p
14
14
  import TMSearchResult from '../search/TMSearchResult';
15
15
  import TMDcmtForm from './TMDcmtForm';
16
16
  import { TMNothingToShow } from './TMDcmtPreview';
17
- import TMButton from '../../base/TMButton';
18
- const StyledNoRelationsOverlay = styled.div `
19
- width: 100%;
20
- height: 100%;
21
- position: fixed;
22
- top: 0;
23
- left: 0;
24
- z-index: 1501;
25
- overflow: visible;
26
- background-color: rgba(0, 0, 0, 0.5);
27
- backdrop-filter: blur(8px);
28
- `;
29
- const StyledNoRelationsPanel = styled.div `
30
- position: absolute;
31
- top: 50%;
32
- left: 50%;
33
- transform: translate(-50%, -50%);
34
- width: 400px;
35
- background: white;
36
- border-radius: 8px;
37
- padding: 20px;
38
- text-align: center;
39
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
40
- display: flex;
41
- flex-direction: column;
42
- align-items: center;
43
- `;
44
- const StyledNoRelationsTitle = styled.h2 `
45
- margin: 0 0 20px 0;
46
- font-size: 1.3em;
47
- color: #333;
48
- `;
49
- const StyledNoRelationsMessage = styled.p `
50
- color: #666;
51
- font-size: 1em;
52
- margin: 0 0 25px 0;
53
- line-height: 1.4;
54
- `;
17
+ import { Spinner } from '../..';
55
18
  const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, deviceType, inputDcmts, isForMaster, showCurrentDcmtIndicator = true, allowNavigation, canNext, canPrev, onNext, onPrev, onBack, appendMasterDcmts, onTaskCreateRequest }) => {
56
- const [id, setID] = useState('');
57
19
  const [focusedItem, setFocusedItem] = useState();
58
20
  const [selectedItems, setSelectedItems] = useState([]);
59
21
  const [showZeroDcmts, setShowZeroDcmts] = useState(false);
60
22
  const [allowMultipleSelection, setAllowMultipleSelection] = useState(false);
61
23
  const [dtdMaster, setDtdMaster] = useState();
62
- const [showNoRelationsModal, setShowNoRelationsModal] = useState(false);
63
- const [shouldGoBackOnClose, setShouldGoBackOnClose] = useState(false);
64
- // Ref to track if this is the first load (vs navigation with onPrev/onNext)
65
- const isFirstLoadRef = useRef(true);
66
- useEffect(() => { setID(genUniqueId()); }, []);
24
+ const [noRelationsOnFirstLoad, setNoRelationsOnFirstLoad] = useState(false);
25
+ const [isCheckingFirstLoad, setIsCheckingFirstLoad] = useState(true);
67
26
  // Load dtdMaster when inputDcmts changes
68
27
  useEffect(() => {
69
28
  const loadDtdMaster = async () => {
@@ -82,23 +41,31 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
82
41
  }, [inputDcmts?.length, inputDcmts?.[0]?.TID, isForMaster]);
83
42
  const handleFocusedItemChanged = useCallback((item) => {
84
43
  setFocusedItem(item);
85
- // When data is loaded and an item is focused, mark as no longer first load
86
44
  if (item) {
87
- isFirstLoadRef.current = false;
45
+ setIsCheckingFirstLoad(false);
88
46
  }
89
47
  }, []);
90
48
  const handleSelectedItemsChanged = useCallback((items) => {
91
49
  setSelectedItems(items);
92
50
  }, []);
93
51
  const handleNoRelationsFound = useCallback(() => {
94
- // Show modal only on first load, not during navigation with onPrev/onNext
95
- if (isFirstLoadRef.current) {
96
- setShowNoRelationsModal(true);
97
- setShouldGoBackOnClose(true);
52
+ if (isCheckingFirstLoad) {
53
+ setNoRelationsOnFirstLoad(true);
98
54
  }
99
- // Mark as no longer first load
100
- isFirstLoadRef.current = false;
101
- }, []);
55
+ }, [isCheckingFirstLoad]);
56
+ // Show warning alert and navigate back when no relations found on first load
57
+ useEffect(() => {
58
+ if (noRelationsOnFirstLoad) {
59
+ setNoRelationsOnFirstLoad(false);
60
+ ShowAlert({
61
+ message: SDKUI_Localizator.RelatedDcmtsNotFound,
62
+ title: SDKUI_Localizator.RelationsNotFound,
63
+ duration: 3000,
64
+ mode: 'warning',
65
+ });
66
+ onBack?.();
67
+ }
68
+ }, [noRelationsOnFirstLoad, onBack]);
102
69
  const commandsMenuItems = [
103
70
  {
104
71
  icon: _jsx(IconMultipleSelection, { color: allowMultipleSelection ? TMColors.tertiary : TMColors.text_normal }),
@@ -123,7 +90,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
123
90
  }
124
91
  }
125
92
  ];
126
- const toolbar = _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [allowMultipleSelection && _jsx("p", { style: { color: TMColors.colorHeader, textAlign: 'center', padding: '1px 4px', borderRadius: '3px', display: 'flex' }, children: `${selectedItems.length} selezionati` }), allowNavigation && canPrev != undefined && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', iconColor: 'white', isModified: false, formMode: FormModes.ReadOnly, canPrev: canPrev, onPrev: onPrev }), allowNavigation && canNext != undefined && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', iconColor: 'white', isModified: false, formMode: FormModes.ReadOnly, canNext: canNext, onNext: onNext }), _jsx(TMContextMenu, { items: commandsMenuItems, trigger: 'left', children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) })] });
93
+ const toolbar = _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [allowMultipleSelection && _jsx("p", { style: { color: TMColors.colorHeader, textAlign: 'center', padding: '1px 4px', borderRadius: '3px', display: 'flex' }, children: `${selectedItems.filter(item => item.isDcmt).length} selezionati` }), allowNavigation && canPrev != undefined && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', iconColor: 'white', isModified: false, formMode: FormModes.ReadOnly, canPrev: canPrev, onPrev: onPrev }), allowNavigation && canNext != undefined && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', iconColor: 'white', isModified: false, formMode: FormModes.ReadOnly, canNext: canNext, onNext: onNext }), _jsx(TMContextMenu, { items: commandsMenuItems, trigger: 'left', children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) })] });
127
94
  const getTitle = () => isForMaster ? `${SDKUI_Localizator.DcmtsMaster} - ${dtdMaster?.nameLoc}` : SDKUI_Localizator.DcmtsDetail;
128
95
  const isMobile = deviceType === DeviceType.MOBILE;
129
96
  const tmTreeView = useMemo(() => _jsx(_Fragment, { children: !inputDcmts || inputDcmts.length === 0
@@ -210,12 +177,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
210
177
  toolbarOptions: { icon: _jsx(IconSearchCheck, { fontSize: 24 }), visible: false, orderNumber: 2, isActive: allInitialPanelVisibility['tmFormOrResult'] }
211
178
  }
212
179
  ], [tmTreeView, tmFormOrResult, focusedItem?.isDcmt, dtdMaster]);
213
- return (_jsxs(_Fragment, { children: [_jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmTreeView', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }), showNoRelationsModal && (_jsx(StyledNoRelationsOverlay, { children: _jsxs(StyledNoRelationsPanel, { children: [_jsx(StyledNoRelationsTitle, { children: SDKUI_Localizator.RelationsNotFound }), _jsx(StyledNoRelationsMessage, { children: SDKUI_Localizator.RelatedDcmtsNotFound }), _jsx(TMButton, { caption: "OK", showTooltip: false, onClick: () => {
214
- setShowNoRelationsModal(false);
215
- if (shouldGoBackOnClose) {
216
- onBack?.();
217
- }
218
- }, btnStyle: "normal" })] }) }))] }));
180
+ return (_jsxs("div", { style: { width: '100%', height: '100%', position: 'relative' }, children: [isCheckingFirstLoad && (_jsx(Spinner, { description: SDKUI_Localizator.Loading, flat: true })), _jsx("div", { style: isCheckingFirstLoad ? { position: 'absolute', width: 0, height: 0, overflow: 'hidden', opacity: 0, pointerEvents: 'none' } : { width: '100%', height: '100%' }, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmTreeView', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) })] }));
219
181
  };
220
182
  export default TMMasterDetailDcmts;
221
183
  /**
@@ -399,7 +399,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
399
399
  /**
400
400
  * Helper function to set up initial tree expansion state.
401
401
  * Called after all data is loaded but before setTreeData.
402
- * Ensures: root container expanded, first document expanded with isRoot=true (for focus), first correlation folder expanded
402
+ * Ensures: root container expanded, first document expanded with isRoot=true (for focus),
403
+ * first correlation folder expanded ONLY if it's the only one (single correlation type)
403
404
  * Returns a NEW tree with the modifications (immutable approach)
404
405
  */
405
406
  const setupInitialTreeExpansion = (tree) => {
@@ -426,9 +427,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
426
427
  expanded: true,
427
428
  isRoot: true
428
429
  };
429
- // 3. Expand first correlation folder (child of the document)
430
+ // 3. Expand first correlation folder ONLY if there's exactly one
430
431
  const docItems = newFirstDoc.items;
431
- if (docItems && docItems.length > 0 && docItems[0].isContainer) {
432
+ const containerChildren = docItems?.filter(child => child.isContainer) ?? [];
433
+ if (containerChildren.length === 1 && docItems && docItems.length > 0 && docItems[0].isContainer) {
432
434
  const newFirstCorrelation = { ...docItems[0], expanded: true };
433
435
  newFirstDoc = {
434
436
  ...newFirstDoc,
@@ -442,10 +444,13 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
442
444
  };
443
445
  }
444
446
  else if (firstDocOrContainer.isContainer) {
445
- // First item is a container (correlation folder) - expand it
447
+ // First item is a container (correlation folder)
448
+ // Count how many sibling containers there are
449
+ const siblingContainers = firstRootItems.filter(child => child.isContainer);
450
+ const shouldExpand = siblingContainers.length === 1;
446
451
  let newFirstCorrelation = {
447
452
  ...firstDocOrContainer,
448
- expanded: true
453
+ expanded: shouldExpand
449
454
  };
450
455
  // Find first document inside this container and mark for focus
451
456
  const containerItems = newFirstCorrelation.items;
@@ -517,8 +522,25 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
517
522
  }
518
523
  }
519
524
  }
520
- // Aggiungi i master come root
521
- tree.push(...masterDocs);
525
+ // Aggiungi i master come root, raggruppando per TID e relationId
526
+ for (const masterContainer of masterDocs) {
527
+ const existingContainer = tree.find(c => c.tid === masterContainer.tid && c.relationId === masterContainer.relationId && c.isContainer);
528
+ if (existingContainer) {
529
+ // Merge documents into existing container, deduplicating by DID
530
+ const existingItems = existingContainer.items ?? [];
531
+ const existingDIDs = new Set(existingItems.map(item => item.did));
532
+ const newItems = (masterContainer.items ?? []).filter(item => !existingDIDs.has(item.did));
533
+ existingContainer.items = [...existingItems, ...newItems];
534
+ existingContainer.itemsCount = existingContainer.items.length;
535
+ existingContainer.isZero = existingContainer.items.length === 0;
536
+ if (masterContainer.searchResult) {
537
+ existingContainer.searchResult = [...(existingContainer.searchResult ?? []), ...masterContainer.searchResult];
538
+ }
539
+ }
540
+ else {
541
+ tree.push(masterContainer);
542
+ }
543
+ }
522
544
  }
523
545
  // ========================================================================
524
546
  // MODALITÀ STANDARD (detail o master invertito)
@@ -530,22 +552,53 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
530
552
  : await getDetailDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel);
531
553
  // Se showMainDocument è false, mostra i container dei dettagli direttamente sotto il container principale
532
554
  if (!showMainDocument) {
533
- // Create type container that contains detail containers directly
534
- const typeContainer = {
535
- key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
536
- name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
537
- tid: dcmt.TID,
538
- dtd,
539
- isContainer: true,
540
- isLoaded: true,
541
- isZero: false,
542
- searchResult: result ? [result] : [],
543
- items: relatedDocs, // Directly show detail containers
544
- itemsCount: relatedDocs.length,
545
- expanded: false,
546
- hidden: false
547
- };
548
- tree.push(typeContainer);
555
+ // Check if a container for this TID already exists in the tree
556
+ const existingContainer = tree.find(c => c.tid === dcmt.TID && c.isContainer);
557
+ if (existingContainer) {
558
+ // Merge relatedDocs into existing container, grouping sub-containers by TID+relationId
559
+ const existingItems = existingContainer.items ?? [];
560
+ for (const relDoc of relatedDocs) {
561
+ const existingSub = existingItems.find(s => s.tid === relDoc.tid && s.relationId === relDoc.relationId && s.isContainer);
562
+ if (existingSub) {
563
+ // Merge documents into existing sub-container, deduplicating by DID
564
+ const subItems = existingSub.items ?? [];
565
+ const existingDIDs = new Set(subItems.map(item => item.did));
566
+ const newItems = (relDoc.items ?? []).filter(item => !existingDIDs.has(item.did));
567
+ existingSub.items = [...subItems, ...newItems];
568
+ existingSub.itemsCount = existingSub.items.length;
569
+ existingSub.isZero = existingSub.items.length === 0;
570
+ if (relDoc.searchResult) {
571
+ existingSub.searchResult = [...(existingSub.searchResult ?? []), ...relDoc.searchResult];
572
+ }
573
+ }
574
+ else {
575
+ existingItems.push(relDoc);
576
+ }
577
+ }
578
+ existingContainer.items = existingItems;
579
+ existingContainer.itemsCount = existingItems.length;
580
+ if (result) {
581
+ existingContainer.searchResult = [...(existingContainer.searchResult ?? []), result];
582
+ }
583
+ }
584
+ else {
585
+ // Create type container that contains detail containers directly
586
+ const typeContainer = {
587
+ key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
588
+ name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
589
+ tid: dcmt.TID,
590
+ dtd,
591
+ isContainer: true,
592
+ isLoaded: true,
593
+ isZero: false,
594
+ searchResult: result ? [result] : [],
595
+ items: relatedDocs, // Directly show detail containers
596
+ itemsCount: relatedDocs.length,
597
+ expanded: false,
598
+ hidden: false
599
+ };
600
+ tree.push(typeContainer);
601
+ }
549
602
  }
550
603
  else {
551
604
  // MODALITÀ ORIGINALE: mostra il documento master come nodo intermedio
@@ -567,22 +620,35 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
567
620
  items: relatedDocs,
568
621
  itemsCount: relatedDocs.length
569
622
  };
570
- // Create type container with unique key
571
- const typeContainer = {
572
- key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
573
- name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
574
- tid: dcmt.TID,
575
- dtd,
576
- isContainer: true,
577
- isLoaded: true,
578
- isZero: false, // Type container is never zero (contains documents)
579
- searchResult: result ? [result] : [],
580
- items: [docNode],
581
- itemsCount: 1,
582
- expanded: false,
583
- hidden: false
584
- };
585
- tree.push(typeContainer);
623
+ // Check if a type container for this TID already exists in the tree
624
+ const existingContainer = tree.find(c => c.tid === dcmt.TID && c.isContainer);
625
+ if (existingContainer) {
626
+ // Add document to existing container
627
+ const existingItems = existingContainer.items ?? [];
628
+ existingContainer.items = [...existingItems, docNode];
629
+ existingContainer.itemsCount = existingContainer.items.length;
630
+ if (result) {
631
+ existingContainer.searchResult = [...(existingContainer.searchResult ?? []), result];
632
+ }
633
+ }
634
+ else {
635
+ // Create type container with unique key
636
+ const typeContainer = {
637
+ key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
638
+ name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
639
+ tid: dcmt.TID,
640
+ dtd,
641
+ isContainer: true,
642
+ isLoaded: true,
643
+ isZero: false, // Type container is never zero (contains documents)
644
+ searchResult: result ? [result] : [],
645
+ items: [docNode],
646
+ itemsCount: 1,
647
+ expanded: false,
648
+ hidden: false
649
+ };
650
+ tree.push(typeContainer);
651
+ }
586
652
  }
587
653
  }
588
654
  // Update progress counter
@@ -1012,7 +1078,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1012
1078
  const metadataContent = customDocumentContent
1013
1079
  ? customDocumentContent(item, defaultMetadataContent || _jsx(_Fragment, {}))
1014
1080
  : defaultMetadataContent;
1015
- return (_jsxs("div", { onDoubleClick: handleDoubleClick, style: documentStyle, children: [item.did && showCurrentDcmtIndicator && item.did === inputDcmts?.[0].DID ? _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' })), metadataContent] }));
1081
+ 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' })), metadataContent] }));
1016
1082
  }, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument]);
1017
1083
  /**
1018
1084
  * Wrapper renderer that handles custom rendering if provided
@@ -1059,6 +1125,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1059
1125
  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); } }) });
1060
1126
  }
1061
1127
  if (mergedTreeData.length === 0) {
1128
+ // If parent handles no-relations via callback, render nothing to avoid UI flash
1129
+ if (onNoRelationsFound)
1130
+ return null;
1062
1131
  return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
1063
1132
  }
1064
1133
  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 }), 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) => {
@@ -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 { PlatformObjectValidator, QueryDescriptor, WhereItem, SDK_Localizator, OrderByItem, SelectItem, SelectItemVisibilities, SDK_Globals, SavedQueryCacheService, SearchEngine, QueryOperators } from '@topconsultnpm/sdk-ts';
3
+ import { PlatformObjectValidator, QueryDescriptor, WhereItem, SDK_Localizator, OrderByItem, SelectItem, SelectItemVisibilities, SDK_Globals, SavedQueryCacheService, SearchEngine, QueryOperators, AccessLevelsEx } from '@topconsultnpm/sdk-ts';
4
4
  import styled from 'styled-components';
5
5
  import TMSearchQueryEditor from './TMSearchQueryEditor';
6
6
  import { getDcmtTypesByQdAsync, SDKUI_Localizator, getQD, IconMenuVertical, IconAddCircleOutline, IconEdit, IconEasy, IconAdvanced, deepCompare, IconSearch, IconClear, getDefaultOperator, prepareQdForSearchAsync, IsParametricQuery, SDKUI_Globals, IconArrowRight, IconMenuCAArchive, getListMaxItems, DEFAULT_MAX_DCMTS_TO_BE_RETURNED } from '../../../helper';
@@ -319,8 +319,8 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
319
319
  { icon: _jsx(IconEdit, {}), name: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QueryWhere}`, beginGroup: true, onClick: () => { setShowFiltersConfig(true); } },
320
320
  { icon: _jsx(IconEdit, {}), name: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QuerySelect}`, onClick: () => { setShowOutputConfig(true); } },
321
321
  { icon: _jsx(IconEdit, {}), name: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QueryOrderBy}`, onClick: () => { setShowOrderByConfig(true); } },
322
- { icon: _jsx(IconMenuCAArchive, { viewBox: '11 11.5 26 27', fontSize: 16, strokeWidth: 2, color: 'black' }), beginGroup: true, name: SDKUI_Localizator.PassToArchive, onClick: handlePassToArchive }
323
- ], [showBackToResultButton, showAdvancedSearch, SQD, onBackToResult, openSqdForm, changeAdvancedSearchAsync, setShowFiltersConfig, setShowOutputConfig, setShowOrderByConfig, handlePassToArchive]);
322
+ { icon: _jsx(IconMenuCAArchive, { viewBox: '11 11.5 26 27', fontSize: 16, strokeWidth: 2, color: 'black' }), beginGroup: true, name: SDKUI_Localizator.PassToArchive, disabled: fromDTD?.perm?.canArchive !== AccessLevelsEx.Yes && fromDTD?.perm?.canArchive !== AccessLevelsEx.Mixed, onClick: handlePassToArchive }
323
+ ], [showBackToResultButton, showAdvancedSearch, SQD, onBackToResult, openSqdForm, changeAdvancedSearchAsync, setShowFiltersConfig, setShowOutputConfig, setShowOrderByConfig, handlePassToArchive, fromDTD?.perm?.canArchive]);
324
324
  const captionText = showAllMdWhere ? SDKUI_Localizator.ShowLess : SDKUI_Localizator.ShowAll;
325
325
  let maxItems = getListMaxItems(deviceType ?? DeviceType.DESKTOP);
326
326
  const diff = (qd?.where?.length ?? 0) - maxItems;