@topconsultnpm/sdkui-react 6.21.0-dev4.9 → 6.21.0-dev5.10

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.
@@ -7,15 +7,18 @@ import { IconMultipleSelection, IconCheckFile, IconDetailDcmts, SDKUI_Localizato
7
7
  import { FormModes, SearchResultContext } from '../../../ts';
8
8
  import { TMColors } from '../../../utils/theme';
9
9
  import ShowAlert from '../../base/TMAlert';
10
- import { DeviceType } from '../../base/TMDeviceProvider';
10
+ import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
11
11
  import { TMSaveFormButtonPrevious, TMSaveFormButtonNext } from '../../forms/TMSaveForm';
12
12
  import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
13
13
  import { TMPanelManagerProvider, useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
14
14
  import TMSearchResult from '../search/TMSearchResult';
15
15
  import TMDcmtForm from './TMDcmtForm';
16
16
  import { TMNothingToShow } from './TMDcmtPreview';
17
- import { Spinner, TMButton } from '../..';
17
+ import { TMButton, TMLayoutWaitingContainer } from '../..';
18
18
  import { useDocumentOperations } from '../../../hooks/useDocumentOperations';
19
+ import TMToppyMessage from '../../../helper/TMToppyMessage';
20
+ import TMPanel from '../../base/TMPanel';
21
+ import sixLogo from '../../../assets/six.png';
19
22
  const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, deviceType, inputDcmts, isForMaster, showCurrentDcmtIndicator = true, allowNavigation, canNext, canPrev, onNext, onPrev, onBack, appendMasterDcmts, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, datagridUtility, dcmtUtility, fetchRemoteCertificates }) => {
20
23
  const floatingBarContainerRef = useRef(null);
21
24
  const [focusedItem, setFocusedItem] = useState();
@@ -27,6 +30,12 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
27
30
  const [isCheckingFirstLoad, setIsCheckingFirstLoad] = useState(true);
28
31
  const [contextMenuVisible, setContextMenuVisible] = useState(false);
29
32
  const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
33
+ // Track if TMRelationViewer is loading (used to disable context menu during loading)
34
+ const [isRelationViewerLoading, setIsRelationViewerLoading] = useState(false);
35
+ // Track if loading has ever been TRUE (to distinguish initial false from post-load false)
36
+ const hasLoadingBeenTrueRef = useRef(false);
37
+ // Track the previous loading state to detect transitions
38
+ const prevIsRelationViewerLoadingRef = useRef(false);
30
39
  const [dtdFocused, setDtdFocused] = useState();
31
40
  const [refreshKey, setRefreshKey] = useState(0);
32
41
  // Separate refresh key for TMFormOrResultWrapper only (doesn't affect tmTreeView)
@@ -35,6 +44,21 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
35
44
  const [focusedItemFormData, setFocusedItemFormData] = useState([]);
36
45
  // Trigger operationItems refresh (after file substitution, etc.)
37
46
  const [refreshOperationsTrigger, setRefreshOperationsTrigger] = useState(0);
47
+ // Safety net: when TMRelationViewer finishes loading (transition from TRUE to FALSE),
48
+ // hide the spinner regardless of whether a document was focused or not.
49
+ // This handles edge cases where no document gets isRoot=true.
50
+ useEffect(() => {
51
+ const prevLoading = prevIsRelationViewerLoadingRef.current;
52
+ prevIsRelationViewerLoadingRef.current = isRelationViewerLoading;
53
+ if (isRelationViewerLoading) {
54
+ // Loading started - mark that we've seen a true state
55
+ hasLoadingBeenTrueRef.current = true;
56
+ }
57
+ else if (prevLoading && hasLoadingBeenTrueRef.current && isCheckingFirstLoad) {
58
+ // Transition from TRUE to FALSE (loading completed) - hide spinner
59
+ setIsCheckingFirstLoad(false);
60
+ }
61
+ }, [isRelationViewerLoading, isCheckingFirstLoad]);
38
62
  // Increments trigger counter to force operationItems to re-calculate
39
63
  const onRefreshOperationsDatagrid = useCallback(async () => {
40
64
  setRefreshOperationsTrigger(prev => prev + 1);
@@ -213,6 +237,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
213
237
  onRefreshAfterAddDcmtToFavs,
214
238
  },
215
239
  });
240
+ const { dcmtOperations: { abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, }, } = features;
216
241
  // Load dtdMaster when inputDcmts changes
217
242
  useEffect(() => {
218
243
  const loadDtdMaster = async () => {
@@ -253,6 +278,7 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
253
278
  useEffect(() => {
254
279
  if (noRelationsOnFirstLoad) {
255
280
  setNoRelationsOnFirstLoad(false);
281
+ setIsCheckingFirstLoad(false); // Hide spinner before navigating back
256
282
  ShowAlert({
257
283
  message: SDKUI_Localizator.RelatedDcmtsNotFound,
258
284
  title: SDKUI_Localizator.RelationsNotFound,
@@ -265,14 +291,14 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
265
291
  const commandsMenuItems = [
266
292
  {
267
293
  icon: _jsx(IconMultipleSelection, { color: allowMultipleSelection ? TMColors.tertiary : TMColors.text_normal }),
268
- name: "Selezione multipla",
294
+ name: allowMultipleSelection ? SDKUI_Localizator.DisableMultipleSelection : SDKUI_Localizator.EnableMultipleSelection,
269
295
  onClick: () => {
270
296
  setAllowMultipleSelection(prev => !prev);
271
297
  }
272
298
  },
273
299
  {
274
- icon: _jsx(IconCheckFile, {}),
275
- name: "Consenti dettagli con 0 documenti",
300
+ icon: _jsx(IconCheckFile, { color: showZeroDcmts ? TMColors.tertiary : TMColors.text_normal }),
301
+ name: showZeroDcmts ? SDKUI_Localizator.HideDetailsWithZeroDocs : SDKUI_Localizator.ShowDetailsWithZeroDocs,
276
302
  onClick: () => {
277
303
  setShowZeroDcmts(prev => !prev);
278
304
  }
@@ -294,17 +320,22 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
294
320
  _jsx(TMNothingToShow, { text: getTitle(), secondText: SDKUI_Localizator.NoDataToDisplay, icon: isForMaster ? _jsx(IconDetailDcmts, { fontSize: 96, transform: 'scale(-1, 1)' }) : _jsx(IconDetailDcmts, { fontSize: 96 }) })
295
321
  :
296
322
  _jsxs("div", { ref: floatingBarContainerRef, style: { width: "100%", height: "100%" }, onContextMenu: (e) => {
323
+ // Disable context menu when loading
324
+ if (isRelationViewerLoading) {
325
+ e.preventDefault();
326
+ return;
327
+ }
297
328
  // Mostra context menu anche sullo spazio bianco (quando non si clicca su un item)
298
329
  e.preventDefault();
299
330
  setContextMenuPosition({ x: e.clientX, y: e.clientY });
300
331
  setContextMenuVisible(true);
301
332
  }, children: [_jsx(TMRelationViewerWrapper, { refreshKey: refreshKey, inputDcmts: inputDcmts, isForMaster: isForMaster, showCurrentDcmtIndicator: showCurrentDcmtIndicator, showZeroDcmts: showZeroDcmts,
302
333
  // customItemRender={customItemRender}
303
- allowMultipleSelection: allowMultipleSelection, focusedItem: focusedItem, selectedItems: selectedItems, onFocusedItemChanged: handleFocusedItemChanged, onSelectedItemsChanged: handleSelectedItemsChanged, onNoRelationsFound: handleNoRelationsFound, onItemContextMenu: onItemContextMenu, focusedItemFormData: focusedItemFormData }), _jsx(TMContextMenu, { items: operationItems, externalControl: {
334
+ allowMultipleSelection: allowMultipleSelection, focusedItem: focusedItem, selectedItems: selectedItems, onFocusedItemChanged: handleFocusedItemChanged, onSelectedItemsChanged: handleSelectedItemsChanged, onNoRelationsFound: handleNoRelationsFound, onItemContextMenu: isRelationViewerLoading ? undefined : onItemContextMenu, focusedItemFormData: focusedItemFormData, onLoadingStateChanged: setIsRelationViewerLoading }), _jsx(TMContextMenu, { items: operationItems, externalControl: {
304
335
  visible: contextMenuVisible,
305
336
  position: contextMenuPosition,
306
337
  onClose: () => setContextMenuVisible(false)
307
- } })] }) }), [inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, allowMultipleSelection, focusedItem, selectedItems, handleFocusedItemChanged, handleSelectedItemsChanged, handleNoRelationsFound, onItemContextMenu, contextMenuVisible, contextMenuPosition, refreshKey, focusedItemFormData]);
338
+ } })] }) }), [inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, allowMultipleSelection, focusedItem, selectedItems, handleFocusedItemChanged, handleSelectedItemsChanged, handleNoRelationsFound, onItemContextMenu, contextMenuVisible, contextMenuPosition, refreshKey, focusedItemFormData, isRelationViewerLoading]);
308
339
  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]);
309
340
  const initialPanelDimensions = {
310
341
  'tmTreeView': { width: '50%', height: '100%' },
@@ -382,7 +413,10 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
382
413
  toolbarOptions: { icon: _jsx(IconSearchCheck, { fontSize: 24 }), visible: false, orderNumber: 2, isActive: allInitialPanelVisibility['tmFormOrResult'] }
383
414
  }
384
415
  ], [tmTreeView, tmFormOrResult, focusedItem?.isDcmt, dtdMaster]);
385
- return (_jsxs("div", { style: { width: '100%', height: '100%', position: 'relative' }, children: [isCheckingFirstLoad && (_jsx(Spinner, { description: SDKUI_Localizator.Loading, flat: true })), _jsxs("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 }) }), renderDcmtOperations, renderFloatingBar] })] }));
416
+ return (_jsxs("div", { style: { width: '100%', height: '100%', position: 'relative' }, children: [isCheckingFirstLoad && (_jsx(TMLoadingOverlay, { onCancel: () => {
417
+ setIsCheckingFirstLoad(false);
418
+ onBack?.();
419
+ } })), _jsxs("div", { style: isCheckingFirstLoad ? { position: 'absolute', width: 0, height: 0, overflow: 'hidden', opacity: 0, pointerEvents: 'none' } : { width: '100%', height: '100%' }, children: [_jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmTreeView', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }), renderDcmtOperations, renderFloatingBar] })] }));
386
420
  };
387
421
  export default TMMasterDetailDcmts;
388
422
  /**
@@ -391,8 +425,37 @@ export default TMMasterDetailDcmts;
391
425
  * - Panel visibility toggling
392
426
  * - Focus delay handling
393
427
  */
394
- const TMRelationViewerWrapper = ({ refreshKey, inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, customItemRender, allowMultipleSelection, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onNoRelationsFound, onItemContextMenu, focusedItemFormData }) => {
428
+ const TMRelationViewerWrapper = ({ refreshKey, inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, customItemRender, allowMultipleSelection, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onNoRelationsFound, onItemContextMenu, focusedItemFormData, onLoadingStateChanged }) => {
395
429
  const { setPanelVisibilityById, setToolbarButtonVisibility } = useTMPanelManagerContext();
430
+ // Monitor device type changes to restore panel visibility when switching from mobile to desktop
431
+ const deviceType = useDeviceType();
432
+ const prevDeviceTypeRef = useRef(deviceType);
433
+ // Restore panel visibility when switching from mobile to desktop
434
+ useEffect(() => {
435
+ const prevDeviceType = prevDeviceTypeRef.current;
436
+ prevDeviceTypeRef.current = deviceType;
437
+ // When switching from mobile to desktop, restore panel visibility based on current focused item
438
+ if (prevDeviceType === DeviceType.MOBILE && deviceType !== DeviceType.MOBILE) {
439
+ // Small delay to let the panel manager complete its internal state update
440
+ setTimeout(() => {
441
+ // Force tmFormOrResult panel to be visible
442
+ setPanelVisibilityById('tmFormOrResult', true);
443
+ // Restore child panel visibility based on focused item
444
+ if (focusedItem?.isDcmt) {
445
+ setPanelVisibilityById('tmSearchResult', false);
446
+ setPanelVisibilityById('tmDcmtForm', true);
447
+ setToolbarButtonVisibility('tmSearchResult', false);
448
+ setToolbarButtonVisibility('tmDcmtForm', true);
449
+ }
450
+ else {
451
+ setPanelVisibilityById('tmSearchResult', true);
452
+ setPanelVisibilityById('tmDcmtForm', false);
453
+ setToolbarButtonVisibility('tmSearchResult', true);
454
+ setToolbarButtonVisibility('tmDcmtForm', false);
455
+ }
456
+ }, 50);
457
+ }
458
+ }, [deviceType, focusedItem, setPanelVisibilityById, setToolbarButtonVisibility]);
396
459
  // Handle focused item changes with panel visibility management
397
460
  const handleFocusedItemChanged = useCallback((item) => {
398
461
  onFocusedItemChanged?.(item);
@@ -422,7 +485,7 @@ const TMRelationViewerWrapper = ({ refreshKey, inputDcmts, isForMaster, showCurr
422
485
  onItemContextMenu?.(item, e);
423
486
  }, 100);
424
487
  }, [onItemContextMenu, handleFocusedItemChanged]);
425
- 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));
488
+ 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, onLoadingStateChanged: onLoadingStateChanged }, refreshKey));
426
489
  };
427
490
  const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid, onRefreshSearchResults, handleNavigateToReference, fetchRemoteCertificates }) => {
428
491
  const { setPanelVisibilityById } = useTMPanelManagerContext();
@@ -430,5 +493,96 @@ const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCrea
430
493
  _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: {
431
494
  onRefreshSearchAsyncDatagrid,
432
495
  }, fetchRemoteCertificates: fetchRemoteCertificates }, refreshKey) :
433
- _jsx(TMSearchResult, { groupId: 'tmFormOrResult', isClosable: deviceType !== DeviceType.MOBILE, context: SearchResultContext.METADATA_SEARCH, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, searchResults: focusedItem?.searchResult ?? [], showSearchResultSidebar: false, showDcmtFormSidebar: false, autoFocusFirstRow: false, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setPanelVisibilityById('tmTreeView', true); }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, editPdfForm: editPdfForm, onOpenPdfEditorRequest: onOpenPdfEditorRequest, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, enablePinIcons: false, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, showBackButton: false, onRefreshSearchAsyncDatagrid: onRefreshSearchResults, onReferenceClick: handleNavigateToReference, fetchRemoteCertificates: fetchRemoteCertificates }, refreshKey) }));
496
+ focusedItem?.searchResult === undefined ? (_jsx(TMPanel, { title: SDKUI_Localizator.SearchResult, children: _jsx(TMToppyMessage, { message: SDKUI_Localizator.SelectDocumentToViewSearchResults }) })) : (_jsx(TMSearchResult, { groupId: 'tmFormOrResult', isClosable: deviceType !== DeviceType.MOBILE, context: SearchResultContext.METADATA_SEARCH, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, searchResults: focusedItem?.searchResult ?? [], showSearchResultSidebar: false, showDcmtFormSidebar: false, autoFocusFirstRow: false, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setPanelVisibilityById('tmTreeView', true); }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, editPdfForm: editPdfForm, onOpenPdfEditorRequest: onOpenPdfEditorRequest, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, enablePinIcons: false, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, showBackButton: false, onRefreshSearchAsyncDatagrid: onRefreshSearchResults, onReferenceClick: handleNavigateToReference, fetchRemoteCertificates: fetchRemoteCertificates }, refreshKey)) }));
497
+ };
498
+ const TMLoadingOverlay = ({ onCancel, description, cancelText }) => {
499
+ return (_jsx("div", { style: {
500
+ position: 'absolute',
501
+ top: 0,
502
+ left: 0,
503
+ width: '100%',
504
+ height: '100%',
505
+ backgroundColor: 'rgba(248, 250, 252, 0.92)',
506
+ zIndex: 1000,
507
+ display: 'flex',
508
+ justifyContent: 'center',
509
+ alignItems: 'center',
510
+ }, children: _jsxs("div", { style: {
511
+ display: 'flex',
512
+ flexDirection: 'column',
513
+ alignItems: 'center',
514
+ gap: '16px',
515
+ padding: '24px 32px',
516
+ background: '#fcfcfc',
517
+ borderRadius: '8px',
518
+ boxShadow: '0 4px 16px rgba(0, 0, 0, 0.1)',
519
+ }, children: [_jsxs("div", { style: { position: 'relative', width: '80px', height: '80px' }, children: [_jsx("img", { style: {
520
+ position: 'absolute',
521
+ top: '50%',
522
+ left: '50%',
523
+ transform: 'translate(-54%, -54%)'
524
+ }, src: sixLogo, width: 35, alt: "" }), _jsx("style", { children: `
525
+ @keyframes tmSpinnerRotate {
526
+ 0% { transform: rotate(0deg); }
527
+ 100% { transform: rotate(360deg); }
528
+ }
529
+ .tm-spinner-animation {
530
+ display: inline-block;
531
+ width: 80px;
532
+ height: 80px;
533
+ position: absolute;
534
+ top: 50%;
535
+ left: 50%;
536
+ transform: translate(-50%, -50%);
537
+ }
538
+ .tm-spinner-animation div {
539
+ animation: tmSpinnerRotate 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
540
+ transform-origin: 40px 40px;
541
+ }
542
+ .tm-spinner-animation div:after {
543
+ content: " ";
544
+ display: block;
545
+ position: absolute;
546
+ width: 7px;
547
+ height: 7px;
548
+ border-radius: 50%;
549
+ background: #0c448e;
550
+ margin: -4px 0 0 -4px;
551
+ }
552
+ .tm-spinner-animation div:nth-child(1) { animation-delay: -0.036s; }
553
+ .tm-spinner-animation div:nth-child(1):after { top: 63px; left: 63px; background: #f7a51f; }
554
+ .tm-spinner-animation div:nth-child(2) { animation-delay: -0.072s; }
555
+ .tm-spinner-animation div:nth-child(2):after { top: 68px; left: 56px; background: #f7a51f; }
556
+ .tm-spinner-animation div:nth-child(3) { animation-delay: -0.108s; }
557
+ .tm-spinner-animation div:nth-child(3):after { top: 71px; left: 48px; background: #d3237b; }
558
+ .tm-spinner-animation div:nth-child(4) { animation-delay: -0.144s; }
559
+ .tm-spinner-animation div:nth-child(4):after { top: 72px; left: 40px; background: #d3237b; }
560
+ .tm-spinner-animation div:nth-child(5) { animation-delay: -0.18s; }
561
+ .tm-spinner-animation div:nth-child(5):after { top: 71px; left: 32px; background: #d12a1c; }
562
+ .tm-spinner-animation div:nth-child(6) { animation-delay: -0.216s; }
563
+ .tm-spinner-animation div:nth-child(6):after { top: 68px; left: 24px; background: #d12a1c; }
564
+ .tm-spinner-animation div:nth-child(7) { animation-delay: -0.252s; }
565
+ .tm-spinner-animation div:nth-child(7):after { top: 63px; left: 17px; background: #782b7d; }
566
+ .tm-spinner-animation div:nth-child(8) { animation-delay: -0.288s; }
567
+ .tm-spinner-animation div:nth-child(8):after { top: 56px; left: 12px; background: #782b7d; }
568
+ ` }), _jsxs("div", { className: "tm-spinner-animation", children: [_jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {}), _jsx("div", {})] })] }), _jsx("span", { style: { fontSize: '14px', color: '#334155', textAlign: 'center' }, children: description ?? SDKUI_Localizator.Loading }), onCancel && (_jsx("button", { onClick: onCancel, style: {
569
+ marginTop: '4px',
570
+ padding: '8px 24px',
571
+ fontSize: '13px',
572
+ fontWeight: 500,
573
+ color: '#64748b',
574
+ background: 'transparent',
575
+ border: '1.5px solid #cbd5e1',
576
+ borderRadius: '6px',
577
+ cursor: 'pointer',
578
+ transition: 'all 0.2s ease',
579
+ }, onMouseEnter: (e) => {
580
+ e.currentTarget.style.background = '#f1f5f9';
581
+ e.currentTarget.style.borderColor = '#94a3b8';
582
+ e.currentTarget.style.color = '#475569';
583
+ }, onMouseLeave: (e) => {
584
+ e.currentTarget.style.background = 'transparent';
585
+ e.currentTarget.style.borderColor = '#cbd5e1';
586
+ e.currentTarget.style.color = '#64748b';
587
+ }, children: cancelText ?? SDKUI_Localizator.Cancel }))] }) }));
434
588
  };
@@ -100,15 +100,18 @@ const TMMergeToPdfForm = (props) => {
100
100
  });
101
101
  return null;
102
102
  }
103
- if (pdfDcmtInfosToDownload.length < MIN_PDF_FOR_MERGE) {
103
+ // Permetti merge anche con 1 solo file se convertibile o metadata-only
104
+ if (!hasEnoughPdfForMerge) {
104
105
  TMMessageBoxManager.show({
105
106
  message: SDKUI_Localizator.OnlyOnePdfOrConvertibleFileMergeBlocked.replaceParams(MIN_PDF_FOR_MERGE.toString()),
106
107
  buttons: [ButtonNames.OK]
107
108
  });
108
109
  return null;
109
110
  }
110
- // Mappa per lookup veloce di FILECOUNT e FILEEXT da selectedItemsFull
111
- const fileInfoMap = new Map(selectedItemsFull.map(d => [`${d.TID}_${d.DID}`, { fileExt: d.FILEEXT, fileCount: d.FILECOUNT }]));
111
+ // Mappa per lookup veloce di FILECOUNT e FILEEXT
112
+ const fileInfoMap = new Map(showTMRelationViewer
113
+ ? selectedItemsRelationViewer.map(d => [`${d.tid}_${d.did}`, { fileExt: d.fileExt ?? null, fileCount: d.fileCount ?? null }])
114
+ : selectedItemsFull.map(d => [`${d.TID}_${d.DID}`, { fileExt: d.FILEEXT, fileCount: d.FILECOUNT }]));
112
115
  // Mappa per i risultati: mantiene l'ordine originale
113
116
  const pdfFilesMap = new Map();
114
117
  // Identifica documenti di soli metadati e documenti con file
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { DcmtTypeDescriptor, SearchResultDescriptor, DataColumnDescriptor } from "@topconsultnpm/sdk-ts";
3
+ import { DcmtMetadataMap } from '../../../helper';
3
4
  import { DcmtInfo, MetadataValueDescriptorEx } from '../../../ts';
4
5
  import { ITMTreeItem } from '../../base/TMTreeView';
5
6
  /**
@@ -19,10 +20,11 @@ export interface RelationTreeItem extends ITMTreeItem {
19
20
  isSeparator?: boolean;
20
21
  isInfoMessage?: boolean;
21
22
  isLogDel?: number;
22
- values?: any;
23
+ values?: DcmtMetadataMap;
23
24
  searchResult?: SearchResultDescriptor[];
24
25
  itemsCount?: number;
25
26
  fileExt?: string;
27
+ fileCount?: number;
26
28
  }
27
29
  /**
28
30
  * Props for TMRelationViewer component
@@ -72,7 +74,10 @@ export interface TMRelationViewerProps {
72
74
  * Value rendering respects DataDomain (uses TMDataListItemViewer for lists, TMDataUserIdItemViewer for users, etc.)
73
75
  */
74
76
  showMetadataNames?: boolean;
75
- /** Maximum depth level for recursive loading (default: 2) */
77
+ /**
78
+ * Maximum depth level for recursive loading (default: 2).
79
+ * Use 0 for unlimited depth (expand as much as possible).
80
+ */
76
81
  maxDepthLevel?: number;
77
82
  /**
78
83
  * If true (default), when isForMaster=true shows: detail doc → master docs as children (inverted navigation)
@@ -122,6 +127,11 @@ export interface TMRelationViewerProps {
122
127
  * (root container + first document + first correlation folder).
123
128
  */
124
129
  defaultExpandAll?: boolean;
130
+ /**
131
+ * Callback invoked when loading state changes.
132
+ * Useful to disable interactions (like context menu) during loading.
133
+ */
134
+ onLoadingStateChanged?: (isLoading: boolean) => void;
125
135
  }
126
136
  /**
127
137
  * Check if document type has detail relations
@@ -131,21 +141,16 @@ export declare const hasDetailRelations: (mTID: number | undefined) => Promise<b
131
141
  * Check if document type has master relations
132
142
  */
133
143
  export declare const hasMasterRelations: (dTID: number | undefined) => Promise<boolean>;
134
- /**
135
- * Get metadata keys excluding system metadata
136
- */
137
- export declare const getMetadataKeys: (obj: any) => string[];
138
- /**
139
- * Get display value keys for a document (max 5, prioritize SYS_Abstract)
140
- */
141
- export declare const getDcmtDisplayValue: (obj: any) => string[];
142
144
  /**
143
145
  * Get display value formatted by column type
144
146
  */
145
147
  export declare const getDisplayValueByColumn: (col: DataColumnDescriptor | undefined, value: any) => any;
146
148
  /**
147
149
  * Convert SearchResultDescriptor to structured data source with metadata
150
+ * For each document, fetches complete metadata using GetMetadataAsync to ensure
151
+ * all metadata properties (isSpecialSearchOutput, permissions, etc.) are available
148
152
  */
149
153
  export declare const searchResultToDataSource: (searchResult: SearchResultDescriptor | undefined, hideSysMetadata?: boolean) => Promise<any[]>;
154
+ export declare const DEFAULT_RELATION_EXPAND_LEVEL = 4;
150
155
  declare const TMRelationViewer: React.FC<TMRelationViewerProps>;
151
156
  export default TMRelationViewer;