@topconsultnpm/sdkui-react 6.21.0-dev1.7 → 6.21.0-dev1.9

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.
@@ -1018,8 +1018,8 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1018
1018
  }
1019
1019
  }, [TID, DID, triggerBlogRefresh, onRefreshBlogDatagrid]);
1020
1020
  const checkoutBadge = useMemo(() => {
1021
- const { cicoEnabled, checkoutStatus } = getDcmtCicoStatus(formData, allUsers, fromDTD);
1022
- if (!cicoEnabled || !checkoutStatus.isCheckedOut)
1021
+ const { checkoutStatus } = getDcmtCicoStatus(formData, allUsers, fromDTD);
1022
+ if (!checkoutStatus.isCheckedOut)
1023
1023
  return null;
1024
1024
  return (_jsx(Ribbon, { "$isMobile": isMobile, children: _jsx(TMTooltip, { content: checkoutStatus.editLockTooltipText, position: "right", children: _jsx("span", { children: checkoutStatus.mode === 'editMode' ? SDKUI_Localizator.CheckOut : 'Locked' }) }) }));
1025
1025
  }, [formData, fromDTD, isMobile]);
@@ -1,9 +1,9 @@
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_Localizator } from '@topconsultnpm/sdk-ts';
3
+ import { DcmtTypeListCacheService, LayoutModes, SDK_Globals, SDK_Localizator } from '@topconsultnpm/sdk-ts';
4
4
  import TMRelationViewer from './TMRelationViewer';
5
5
  import TMContextMenu from '../../NewComponents/ContextMenu/TMContextMenu';
6
- import { IconMultipleSelection, IconCheckFile, IconDetailDcmts, SDKUI_Localizator, IconMenuVertical, IconDataList, IconPreview, IconSearchCheck, IconBoard, IconDcmtTypeSys, IconShow, getMoreInfoTasksForDocument, isApprovalWorkflowView } from '../../../helper';
6
+ import { IconMultipleSelection, IconCheckFile, IconDetailDcmts, SDKUI_Localizator, IconMenuVertical, IconDataList, IconPreview, IconSearchCheck, IconBoard, IconDcmtTypeSys, IconShow, getMoreInfoTasksForDocument, isApprovalWorkflowView, searchResultToMetadataValues } from '../../../helper';
7
7
  import { FormModes, SearchResultContext } from '../../../ts';
8
8
  import { TMColors } from '../../../utils/theme';
9
9
  import ShowAlert from '../../base/TMAlert';
@@ -29,11 +29,60 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
29
29
  const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, y: 0 });
30
30
  const [dtdFocused, setDtdFocused] = useState();
31
31
  const [refreshKey, setRefreshKey] = useState(0);
32
+ // Stato per gestire la transizione fluida durante il refresh
33
+ const [isRefreshing, setIsRefreshing] = useState(false);
34
+ /** State for transformed focusedItem metadata values (similar to formData in TMDcmtForm) */
35
+ const [focusedItemFormData, setFocusedItemFormData] = useState([]);
36
+ // Trigger operationItems refresh (after file substitution, etc.)
37
+ const [refreshOperationsTrigger, setRefreshOperationsTrigger] = useState(0);
38
+ // Increments trigger counter to force operationItems to re-calculate
39
+ const onRefreshOperationsDatagrid = useCallback(async () => {
40
+ setRefreshOperationsTrigger(prev => prev + 1);
41
+ }, []);
42
+ // Refresh con transizione fluida: fade-out -> update -> fade-in
32
43
  const onRefreshSearch = async () => {
33
44
  await dcmtUtility?.onRefreshPreviewForm?.();
34
- // forza il refresh del form di dettaglio al salvataggio
35
- setRefreshKey(prev => prev + 1);
45
+ // Avvia fade-out
46
+ setIsRefreshing(true);
47
+ // Attendi che il fade-out sia completato, poi aggiorna
48
+ setTimeout(() => {
49
+ setRefreshKey(prev => prev + 1);
50
+ onRefreshOperationsDatagrid();
51
+ // Attendi un po' per dare tempo al re-render, poi fade-in
52
+ setTimeout(() => {
53
+ setIsRefreshing(false);
54
+ }, 300); // Durata extra dell'overlay dopo l'update
55
+ }, 200); // Durata del fade-out
36
56
  };
57
+ useEffect(() => {
58
+ const fetchFocusedItemMetadata = async () => {
59
+ if (!focusedItem?.tid || !focusedItem?.did) {
60
+ setFocusedItemFormData([]);
61
+ return;
62
+ }
63
+ try {
64
+ const tid = focusedItem?.tid;
65
+ const did = focusedItem?.did;
66
+ const metadata = await SDK_Globals.tmSession?.NewSearchEngine().GetMetadataAsync(tid, did, true);
67
+ // Transform metadata to MetadataValueDescriptorEx[] (similar to setMetadataList in TMDcmtForm)
68
+ if (metadata) {
69
+ const dtdResult = metadata.dtdResult;
70
+ const rows = dtdResult?.rows ? dtdResult.rows[0] : [];
71
+ const mids = metadata.selectMIDs;
72
+ // Get DTD with metadata descriptors
73
+ const dtdWithMetadata = await DcmtTypeListCacheService.GetWithNotGrantedAsync(tid, did, metadata);
74
+ const mdList = dtdWithMetadata?.metadata ?? [];
75
+ const metadataList = searchResultToMetadataValues(tid, dtdResult, rows, mids, mdList, LayoutModes.Update);
76
+ setFocusedItemFormData(structuredClone(metadataList));
77
+ }
78
+ }
79
+ catch (error) {
80
+ console.error('Error fetching focusedItem metadata:', error);
81
+ setFocusedItemFormData([]);
82
+ }
83
+ };
84
+ fetchFocusedItemMetadata();
85
+ }, [focusedItem?.tid, focusedItem?.did, refreshOperationsTrigger]);
37
86
  // Load dtdFocused when focusedItem changes
38
87
  useEffect(() => {
39
88
  const loadDtdFocused = async () => {
@@ -83,14 +132,23 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
83
132
  currentMetadataValues: [],
84
133
  allUsers: [],
85
134
  // searchResult: selectedSearchResult,
86
- datagridUtility,
135
+ datagridUtility: {
136
+ visibleItems: [],
137
+ onRefreshSearchAsyncDatagrid: datagridUtility?.onRefreshSearchAsyncDatagrid,
138
+ onRefreshDataRowsAsync: datagridUtility?.onRefreshDataRowsAsync,
139
+ refreshFocusedDataRowAsync: datagridUtility?.refreshFocusedDataRowAsync,
140
+ onRefreshBlogDatagrid: datagridUtility?.onRefreshBlogDatagrid,
141
+ onRefreshPreviewDatagrid: datagridUtility?.onRefreshPreviewDatagrid,
142
+ refreshOperationsTrigger,
143
+ onRefreshOperationsDatagrid,
144
+ },
87
145
  dcmtUtility: {
88
146
  approvalVID: dcmtUtility?.approvalVID,
89
- dcmtDataRowForCicoStatus: dcmtUtility?.dcmtDataRowForCicoStatus,
147
+ dcmtDataRowForCicoStatus: focusedItemFormData, // Passa i metadata trasformati del focusedItem per le operazioni di CICO
90
148
  selectedDcmtSearchResultRelations: dcmtUtility?.selectedDcmtSearchResultRelations,
91
149
  dcmtTIDHasDetailRelations: dcmtUtility?.dcmtTIDHasDetailRelations,
92
150
  dcmtTIDHasMasterRelations: dcmtUtility?.dcmtTIDHasMasterRelations,
93
- updateCurrentDcmt: dcmtUtility?.updateCurrentDcmt,
151
+ updateCurrentDcmt: onRefreshSearch,
94
152
  onCloseDcmtForm: dcmtUtility?.onCloseDcmtForm,
95
153
  onRefreshBlogForm: dcmtUtility?.onRefreshBlogForm,
96
154
  onRefreshPreviewForm: onRefreshSearch,
@@ -225,14 +283,14 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
225
283
  e.preventDefault();
226
284
  setContextMenuPosition({ x: e.clientX, y: e.clientY });
227
285
  setContextMenuVisible(true);
228
- }, children: [_jsx(TMRelationViewerWrapper, { inputDcmts: inputDcmts, isForMaster: isForMaster, showCurrentDcmtIndicator: showCurrentDcmtIndicator, showZeroDcmts: showZeroDcmts,
286
+ }, children: [_jsx(TMRelationViewerWrapper, { refreshKey: refreshKey, inputDcmts: inputDcmts, isForMaster: isForMaster, showCurrentDcmtIndicator: showCurrentDcmtIndicator, showZeroDcmts: showZeroDcmts,
229
287
  // customItemRender={customItemRender}
230
- allowMultipleSelection: allowMultipleSelection, focusedItem: focusedItem, selectedItems: selectedItems, onFocusedItemChanged: handleFocusedItemChanged, onSelectedItemsChanged: handleSelectedItemsChanged, onNoRelationsFound: handleNoRelationsFound, onItemContextMenu: onItemContextMenu }, refreshKey), _jsx(TMContextMenu, { items: operationItems, externalControl: {
288
+ allowMultipleSelection: allowMultipleSelection, focusedItem: focusedItem, selectedItems: selectedItems, onFocusedItemChanged: handleFocusedItemChanged, onSelectedItemsChanged: handleSelectedItemsChanged, onNoRelationsFound: handleNoRelationsFound, onItemContextMenu: onItemContextMenu, focusedItemFormData: focusedItemFormData }), _jsx(TMContextMenu, { items: operationItems, externalControl: {
231
289
  visible: contextMenuVisible,
232
290
  position: contextMenuPosition,
233
291
  onClose: () => setContextMenuVisible(false)
234
- } })] }) }), [inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, allowMultipleSelection, focusedItem, selectedItems, handleFocusedItemChanged, handleSelectedItemsChanged, handleNoRelationsFound, onItemContextMenu, contextMenuVisible, contextMenuPosition, refreshKey]);
235
- const tmFormOrResult = useMemo(() => _jsx(TMFormOrResultWrapper, { 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 }, refreshKey), [focusedItem, deviceType, allTasks, handleNavigateToWGs, handleNavigateToDossiers, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshAfterAddDcmtToFavs, refreshKey]);
292
+ } })] }) }), [inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, allowMultipleSelection, focusedItem, selectedItems, handleFocusedItemChanged, handleSelectedItemsChanged, handleNoRelationsFound, onItemContextMenu, contextMenuVisible, contextMenuPosition, refreshKey, focusedItemFormData]);
293
+ const tmFormOrResult = useMemo(() => _jsx(TMFormOrResultWrapper, { refreshKey: refreshKey, 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 }), [focusedItem, deviceType, allTasks, handleNavigateToWGs, handleNavigateToDossiers, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshAfterAddDcmtToFavs, refreshKey]);
236
294
  const initialPanelDimensions = {
237
295
  'tmTreeView': { width: '50%', height: '100%' },
238
296
  'tmFormOrResult': { width: '50%', height: '100%' },
@@ -309,7 +367,28 @@ const TMMasterDetailDcmts = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallba
309
367
  toolbarOptions: { icon: _jsx(IconSearchCheck, { fontSize: 24 }), visible: false, orderNumber: 2, isActive: allInitialPanelVisibility['tmFormOrResult'] }
310
368
  }
311
369
  ], [tmTreeView, tmFormOrResult, focusedItem?.isDcmt, dtdMaster]);
312
- 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] })] }));
370
+ 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 }) }), _jsxs("div", { style: {
371
+ position: 'absolute',
372
+ top: 0,
373
+ left: 0,
374
+ right: 0,
375
+ bottom: 0,
376
+ backgroundColor: 'rgba(255, 255, 255, 0.7)',
377
+ display: 'flex',
378
+ alignItems: 'center',
379
+ justifyContent: 'center',
380
+ opacity: isRefreshing ? 1 : 0,
381
+ pointerEvents: isRefreshing ? 'auto' : 'none',
382
+ transition: 'opacity 200ms ease-in-out',
383
+ zIndex: 10,
384
+ }, children: [_jsx("div", { style: {
385
+ width: 40,
386
+ height: 40,
387
+ border: '3px solid #e0e0e0',
388
+ borderTopColor: TMColors.primaryColor,
389
+ borderRadius: '50%',
390
+ animation: 'spin 0.8s linear infinite',
391
+ } }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` })] }), renderDcmtOperations, renderFloatingBar] })] }));
313
392
  };
314
393
  export default TMMasterDetailDcmts;
315
394
  /**
@@ -318,7 +397,7 @@ export default TMMasterDetailDcmts;
318
397
  * - Panel visibility toggling
319
398
  * - Focus delay handling
320
399
  */
321
- const TMRelationViewerWrapper = ({ inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, customItemRender, allowMultipleSelection, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onNoRelationsFound, onItemContextMenu }) => {
400
+ const TMRelationViewerWrapper = ({ refreshKey, inputDcmts, isForMaster, showCurrentDcmtIndicator, showZeroDcmts, customItemRender, allowMultipleSelection, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onNoRelationsFound, onItemContextMenu, focusedItemFormData }) => {
322
401
  const { setPanelVisibilityById, setToolbarButtonVisibility } = useTMPanelManagerContext();
323
402
  // Handle focused item changes with panel visibility management
324
403
  const handleFocusedItemChanged = useCallback((item) => {
@@ -349,13 +428,13 @@ const TMRelationViewerWrapper = ({ inputDcmts, isForMaster, showCurrentDcmtIndic
349
428
  onItemContextMenu?.(item, e);
350
429
  }, 100);
351
430
  }, [onItemContextMenu, handleFocusedItemChanged]);
352
- 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 }));
431
+ 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));
353
432
  };
354
- const TMFormOrResultWrapper = ({ deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid }) => {
433
+ const TMFormOrResultWrapper = ({ refreshKey, deviceType, focusedItem, onTaskCreateRequest, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, onRefreshAfterAddDcmtToFavs, editPdfForm, openS4TViewer, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onRefreshSearchAsyncDatagrid }) => {
355
434
  const { setPanelVisibilityById } = useTMPanelManagerContext();
356
435
  return (_jsx(_Fragment, { children: focusedItem?.isDcmt ?
357
436
  _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: {
358
437
  onRefreshSearchAsyncDatagrid,
359
- } }) :
360
- _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 }) }));
438
+ } }, refreshKey) :
439
+ _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 }, refreshKey) }));
361
440
  };
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { DcmtTypeDescriptor, SearchResultDescriptor, DataColumnDescriptor } from "@topconsultnpm/sdk-ts";
3
- import { DcmtInfo } from '../../../ts';
3
+ import { DcmtInfo, MetadataValueDescriptorEx } from '../../../ts';
4
4
  import { ITMTreeItem } from '../../base/TMTreeView';
5
5
  /**
6
6
  * Tree item structure for relations
@@ -104,6 +104,11 @@ export interface TMRelationViewerProps {
104
104
  * Use to show a context menu.
105
105
  */
106
106
  onItemContextMenu?: (item: RelationTreeItem, e: React.MouseEvent) => void;
107
+ /**
108
+ * Metadata values for the focused item
109
+ * (used in master-detail context)
110
+ */
111
+ focusedItemFormData?: MetadataValueDescriptorEx[];
107
112
  }
108
113
  /**
109
114
  * Check if document type has detail relations
@@ -1,7 +1,7 @@
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
- import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataFormats, SystemMIDs, MetadataDataDomains, RelationCacheService, RelationTypes } from "@topconsultnpm/sdk-ts";
4
- import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo } from '../../../helper';
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';
5
5
  import { TMColors } from '../../../utils/theme';
6
6
  import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
7
7
  import TMTreeView from '../../base/TMTreeView';
@@ -136,7 +136,7 @@ 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, }) => {
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 = [] }) => {
140
140
  // State
141
141
  const [dcmtTypes, setDcmtTypes] = useState([]);
142
142
  const [treeData, setTreeData] = useState([]);
@@ -160,6 +160,16 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
160
160
  const userInteractedWithStaticItemsRef = React.useRef(false);
161
161
  // Ref to track the last inputKey for which we set the focused item
162
162
  const lastFocusedInputRef = React.useRef('');
163
+ // State for all users (used for checkout status display)
164
+ const [allUsers, setAllUsers] = useState([]);
165
+ // Load all users for checkout status resolution
166
+ useEffect(() => {
167
+ const fetchAllUsers = async () => {
168
+ const users = await UserListCacheService.GetAllAsync();
169
+ setAllUsers(users ?? []);
170
+ };
171
+ fetchAllUsers();
172
+ }, []);
163
173
  /**
164
174
  * Generate a stable key from inputDcmts to detect real changes
165
175
  */
@@ -234,6 +244,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
234
244
  dcmtDetails.push({
235
245
  tid: tid,
236
246
  did: did,
247
+ dtd: dtd,
237
248
  isLogDel: isLogDel,
238
249
  key: `${tid}_${did}_${searchResult.relationID}_${mTID}_${mDID}_${rowGUID}`,
239
250
  isDcmt: true,
@@ -247,7 +258,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
247
258
  expanded: false,
248
259
  hidden: false,
249
260
  name: row?.SYS_Abstract?.value || row?.SYS_SUBJECT?.value || `Documento ${did}`,
250
- fileExt: row?.FILEEXT?.value
261
+ fileExt: row?.FILEEXT?.value,
251
262
  // Note: Recursive loading on expansion is handled by calculateItemsForNode
252
263
  });
253
264
  }
@@ -260,7 +271,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
260
271
  console.error('❌ Error loading detail documents:', error);
261
272
  }
262
273
  return items;
263
- }, [allowedTIDs]);
274
+ }, [allowedTIDs, focusedItemFormData, focusedItem?.dtd?.id]);
264
275
  /**
265
276
  * Recursively retrieve master documents
266
277
  */
@@ -319,6 +330,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
319
330
  dcmtMasters.push({
320
331
  tid: tid,
321
332
  did: did,
333
+ dtd: dtd,
322
334
  isLogDel: isLogDel,
323
335
  key: `${tid}_${did}_${searchResult.relationID}_${dTID}_${dDID}_${rowGUID}`,
324
336
  isDcmt: true,
@@ -609,6 +621,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
609
621
  name: docRow?.SYS_Abstract?.value || docRow?.SYS_SUBJECT?.value || `Documento ${isForMaster ? 'Dettaglio' : 'Master'}`,
610
622
  tid: dcmt.TID,
611
623
  did: dcmt.DID,
624
+ dtd: dtd,
612
625
  isDcmt: true,
613
626
  isContainer: false,
614
627
  expanded: false,
@@ -1080,8 +1093,31 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
1080
1093
  const metadataContent = customDocumentContent
1081
1094
  ? customDocumentContent(item, defaultMetadataContent || _jsx(_Fragment, {}))
1082
1095
  : defaultMetadataContent;
1083
- 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] }));
1084
- }, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument]);
1096
+ // Calculate checkout status for non-root documents
1097
+ let checkoutStatusIcon = null;
1098
+ if (item.values && item.dtd) {
1099
+ // Convert item.values to flat format expected by getDcmtCicoStatus
1100
+ const flatValues = {};
1101
+ if (item.values) {
1102
+ for (const key of Object.keys(item.values)) {
1103
+ const entry = item.values[key];
1104
+ if (entry?.md?.id && item.tid) {
1105
+ // Create TID_MID key format for metadata
1106
+ flatValues[`${item.tid}_${entry.md.id}`] = entry.value;
1107
+ }
1108
+ // Also add direct key for system properties
1109
+ flatValues[key] = entry?.value;
1110
+ }
1111
+ flatValues.TID = item.tid;
1112
+ flatValues.DID = item.did;
1113
+ }
1114
+ const { checkoutStatus } = getDcmtCicoStatus(flatValues, allUsers, item.dtd);
1115
+ if (checkoutStatus.isCheckedOut && checkoutStatus.icon) {
1116
+ checkoutStatusIcon = checkoutStatus.icon;
1117
+ }
1118
+ }
1119
+ 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] }));
1120
+ }, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument, focusedItemFormData, focusedItem?.dtd, allUsers]);
1085
1121
  /**
1086
1122
  * Wrapper renderer that handles custom rendering if provided
1087
1123
  */
@@ -22,7 +22,7 @@ export interface CheckoutInfo {
22
22
  checkoutFolder: string;
23
23
  checkoutName: string;
24
24
  }
25
- interface CheckoutStatusResult {
25
+ export interface CheckoutStatusResult {
26
26
  isCheckedOut: boolean;
27
27
  mode: 'editMode' | 'lockMode' | '';
28
28
  version: number;
@@ -259,13 +259,15 @@ export const getDcmtCicoStatus = (dcmt, allUsers, dtd) => {
259
259
  let checkoutDate;
260
260
  let version = 1;
261
261
  let fileExt;
262
+ let isMetadata = false;
262
263
  // ========================================================================
263
- // CASO 1: Documento come Array di MetadataValueDescriptorEx
264
+ // CASO 1: Documento come Array di MetadataValueDescriptorEx: Form del documento
264
265
  // ========================================================================
265
266
  // Questo formato viene utilizzato quando il documento proviene da query
266
267
  // o liste dove ogni metadato è un oggetto separato con proprietà 'md' e 'value'
267
268
  if (Array.isArray(dcmt) && dcmt.length > 0 && dcmt[0]?.md !== undefined) {
268
269
  const dcmtsArray = dcmt;
270
+ console.log("dcmtsArray:", dcmtsArray);
269
271
  // Estrai l'ID dell'utente che ha effettuato il checkout
270
272
  const checkoutUserIdProperty = dcmtsArray.find((item) => item.md?.name === CICO_MetadataNames.CICO_CheckoutUserID);
271
273
  const checkoutUserIdValue = checkoutUserIdProperty?.value;
@@ -279,9 +281,11 @@ export const getDcmtCicoStatus = (dcmt, allUsers, dtd) => {
279
281
  version = (versionRaw != null && !isNaN(Number(versionRaw))) ? Number(versionRaw) : 1;
280
282
  const fileExtProperty = dcmtsArray.find((item) => item.mid === SystemMIDsAsNumber.FileExt);
281
283
  fileExt = fileExtProperty?.value ? fileExtProperty.value.toString() : null;
284
+ const fileCountProperty = dcmtsArray.find((item) => item.mid === SystemMIDsAsNumber.FileCount);
285
+ isMetadata = !(fileCountProperty?.value && Number(fileCountProperty.value) > 0);
282
286
  }
283
287
  // ========================================================================
284
- // CASO 2: Documento come Oggetto Piatto (formato standard)
288
+ // CASO 2: Documento come Oggetto Piatto (formato standard): Risultato della ricerca
285
289
  // ========================================================================
286
290
  // Questo formato viene utilizzato quando il documento ha proprietà dirette
287
291
  // nel formato chiave-valore: TID, DID, e "TID_MetadataID" per i metadati
@@ -290,6 +294,8 @@ export const getDcmtCicoStatus = (dcmt, allUsers, dtd) => {
290
294
  const CICO_CheckoutUserID_Meta = dtd.metadata?.find(md => md.name === CICO_MetadataNames.CICO_CheckoutUserID);
291
295
  const CICO_CheckoutDate_Meta = dtd.metadata?.find(md => md.name === CICO_MetadataNames.CICO_CheckoutDate);
292
296
  const CICO_Version_Meta = dtd.metadata?.find(md => md.name === CICO_MetadataNames.CICO_Version);
297
+ const fileCountValue = dcmt.FILECOUNT != null ? Number(dcmt.FILECOUNT) : NaN;
298
+ isMetadata = isNaN(fileCountValue) || fileCountValue <= 0;
293
299
  fileExt = dcmt.FILEEXT ? dcmt.FILEEXT.toString() : null;
294
300
  // Estrai l'ID dell'utente che ha effettuato il checkout
295
301
  if (CICO_CheckoutUserID_Meta?.id) {
@@ -310,6 +316,15 @@ export const getDcmtCicoStatus = (dcmt, allUsers, dtd) => {
310
316
  }
311
317
  }
312
318
  // ========================================================================
319
+ // EARLY RETURN: Documento di soli metadati (senza file)
320
+ // ========================================================================
321
+ if (isMetadata) {
322
+ return {
323
+ cicoEnabled: false,
324
+ checkoutStatus: { isCheckedOut: false, mode: '', version: 1, icon: null, editLockTooltipText: null }
325
+ };
326
+ }
327
+ // ========================================================================
313
328
  // COSTRUZIONE DELLO STATO DI CHECKOUT
314
329
  // ========================================================================
315
330
  let checkoutStatus = {
@@ -345,8 +360,8 @@ export const getDcmtCicoStatus = (dcmt, allUsers, dtd) => {
345
360
  // RESTITUZIONE RISULTATO FINALE
346
361
  // ========================================================================
347
362
  return {
348
- // CICO è abilitato se configurato nel DTD e l'utente ha i permessi
349
- cicoEnabled: cicoInfo.CICO === 1 && cicoInfo.CanCICO === AccessLevels.Yes && fileExt !== null && fileExt !== '',
363
+ // CICO è abilitato se configurato nel DTD, l'utente ha i permessi e il documento non è di soli metadati
364
+ cicoEnabled: cicoInfo.CICO === 1 && cicoInfo.CanCICO === AccessLevels.Yes && fileExt !== null && fileExt !== '' && !isMetadata,
350
365
  checkoutStatus: checkoutStatus
351
366
  };
352
367
  };
@@ -105,6 +105,7 @@ export const useDocumentOperations = (props) => {
105
105
  }, [refreshOperationsTrigger]);
106
106
  // Context helpers
107
107
  const isDcmtFormContext = context === SearchResultContext.DCMT_FORM;
108
+ const isMasterDetailContext = context === SearchResultContext.MASTER_DETAIL;
108
109
  const { showHistory, showHistoryCallback, hideHistoryCallback, showCheckoutInformationForm, commentFormState, hideCommentFormCallback, showCheckoutInformationFormCallback, hideCheckoutInformationFormCallback, copyCheckoutPathToClipboardCallback, handleCheckOutCallback, handleCheckInCallback, showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, } = useCheckInOutOperations({
109
110
  onRefreshPreview: onRefreshPreviewCallback
110
111
  });
@@ -632,7 +633,7 @@ export const useDocumentOperations = (props) => {
632
633
  const checkinMenuItem = () => {
633
634
  // Take the first document (used for validation checks)
634
635
  let dcmt = focusedItem;
635
- if (isDcmtFormContext) {
636
+ if (isDcmtFormContext || isMasterDetailContext) {
636
637
  dcmt = dcmtDataRowForCicoStatus;
637
638
  }
638
639
  const { cicoEnabled, checkoutStatus } = getDcmtCicoStatus(dcmt, allUsers, dtd);
@@ -1059,7 +1060,6 @@ export const useDocumentOperations = (props) => {
1059
1060
  disabled: isDisabledForSingleRow() && isDisabledForMultiRow(),
1060
1061
  submenu: [
1061
1062
  addToFavoriteOperation(),
1062
- openFormOperation(),
1063
1063
  downloadFileMenuItem(),
1064
1064
  downloadXMLAttachmentsMenuItem(),
1065
1065
  ]
@@ -1090,6 +1090,7 @@ export const useDocumentOperations = (props) => {
1090
1090
  ]
1091
1091
  },
1092
1092
  signatureMenuItem(),
1093
+ checkinMenuItem(),
1093
1094
  ...((inputDcmtFormLayoutMode === LayoutModes.Update) ? [fullTextSearchMenuItem()] : []),
1094
1095
  ];
1095
1096
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.21.0-dev1.7",
3
+ "version": "6.21.0-dev1.9",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",