@topconsultnpm/sdkui-react 6.21.0-dev3.8 → 6.21.0-dev4.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.
Files changed (63) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +28 -2
  2. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +6 -6
  3. package/lib/components/base/TMAreaManager.js +11 -0
  4. package/lib/components/base/TMDataGrid.js +12 -2
  5. package/lib/components/base/TMDataGridExportForm.js +19 -8
  6. package/lib/components/base/TMModal.js +2 -2
  7. package/lib/components/base/TMPopUp.d.ts +1 -0
  8. package/lib/components/base/TMPopUp.js +59 -2
  9. package/lib/components/base/TMSpinner.d.ts +4 -2
  10. package/lib/components/base/TMSpinner.js +33 -6
  11. package/lib/components/choosers/TMDistinctValues.js +1 -1
  12. package/lib/components/choosers/TMDynDataListItemChooser.js +6 -1
  13. package/lib/components/choosers/TMGroupChooser.js +1 -1
  14. package/lib/components/editors/TMEditorStyled.d.ts +4 -4
  15. package/lib/components/editors/TMFormulaEditor.d.ts +1 -0
  16. package/lib/components/editors/TMFormulaEditor.js +98 -49
  17. package/lib/components/editors/TMMetadataEditor.js +6 -2
  18. package/lib/components/editors/TMMetadataValues.js +23 -6
  19. package/lib/components/editors/TMTextBox.d.ts +1 -0
  20. package/lib/components/editors/TMTextBox.js +2 -1
  21. package/lib/components/features/documents/TMDcmtForm.d.ts +2 -0
  22. package/lib/components/features/documents/TMDcmtForm.js +2 -1
  23. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  24. package/lib/components/features/documents/TMMasterDetailDcmts.js +7 -4
  25. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +2 -2
  26. package/lib/components/features/documents/TMMergeToPdfForm.js +91 -48
  27. package/lib/components/features/documents/TMRelationViewer.js +58 -5
  28. package/lib/components/features/documents/mergePdfUtils.d.ts +52 -0
  29. package/lib/components/features/documents/mergePdfUtils.js +268 -0
  30. package/lib/components/features/search/TMMetadataOutputForm.d.ts +17 -0
  31. package/lib/components/features/search/TMMetadataOutputForm.js +225 -0
  32. package/lib/components/features/search/TMMetadataSorterForm.d.ts +17 -0
  33. package/lib/components/features/search/TMMetadataSorterForm.js +243 -0
  34. package/lib/components/features/search/TMSearch.d.ts +2 -0
  35. package/lib/components/features/search/TMSearch.js +2 -2
  36. package/lib/components/features/search/TMSearchQueryEditor.js +14 -8
  37. package/lib/components/features/search/TMSearchQueryPanel.js +249 -58
  38. package/lib/components/features/search/TMSearchResult.d.ts +3 -0
  39. package/lib/components/features/search/TMSearchResult.js +77 -22
  40. package/lib/components/features/search/metadataFormHelper.d.ts +16 -0
  41. package/lib/components/features/search/metadataFormHelper.js +77 -0
  42. package/lib/components/forms/Login/TMLoginForm.js +15 -3
  43. package/lib/components/wizard/TMWizard.d.ts +1 -0
  44. package/lib/components/wizard/TMWizard.js +5 -3
  45. package/lib/helper/Enum_Localizator.js +2 -0
  46. package/lib/helper/SDKUI_Localizator.d.ts +14 -0
  47. package/lib/helper/SDKUI_Localizator.js +152 -12
  48. package/lib/helper/certificateImportHelper.d.ts +43 -0
  49. package/lib/helper/certificateImportHelper.js +403 -0
  50. package/lib/helper/checkinCheckoutManager.js +10 -2
  51. package/lib/helper/helpers.d.ts +2 -1
  52. package/lib/helper/helpers.js +1 -0
  53. package/lib/helper/index.d.ts +1 -0
  54. package/lib/helper/index.js +1 -0
  55. package/lib/hooks/useDataUserIdItem.js +1 -1
  56. package/lib/hooks/useDcmtOperations.d.ts +2 -1
  57. package/lib/hooks/useDcmtOperations.js +177 -4
  58. package/lib/hooks/useDocumentOperations.d.ts +2 -0
  59. package/lib/hooks/useDocumentOperations.js +47 -6
  60. package/lib/services/platform_services.d.ts +4 -4
  61. package/lib/ts/types.d.ts +3 -1
  62. package/lib/ts/types.js +2 -0
  63. package/package.json +13 -7
@@ -54,15 +54,20 @@ const TMSearchResult = ({
54
54
  // Data
55
55
  groupId, searchResults = [], context = SearchResultContext.METADATA_SEARCH, title, selectedSearchResultTID, floatingActionConfig, workingGroupContext = undefined, inputDID,
56
56
  // Boolean flags to enable/disable features
57
- formAutoOpen, isVisible = true, allowRelations = true, openDcmtFormAsModal = false, showSearchResultSidebar = true, showDcmtFormSidebar = true, showSelector = false, isClosable = false, allowFloatingBar = true, showToolbarHeader = true, showBackButton = true, disableAccordionIfSingleCategory = false, editPdfForm = false, openS4TViewer = false, showTodoDcmtForm = false, showToppyDraggableHelpCenter = true, toppyHelpCenterUsePortal = false, showNoDcmtFoundMessage = true, enablePinIcons = true,
57
+ autoFocusFirstRow = true, formAutoOpen, isVisible = true, allowRelations = true, openDcmtFormAsModal = false, showSearchResultSidebar = true, showDcmtFormSidebar = true, showSelector = false, isClosable = false, allowFloatingBar = true, showToolbarHeader = true, showBackButton = true, disableAccordionIfSingleCategory = false, editPdfForm = false, openS4TViewer = false, showTodoDcmtForm = false, showToppyDraggableHelpCenter = true, toppyHelpCenterUsePortal = false, showNoDcmtFoundMessage = true, enablePinIcons = true,
58
58
  // Callbacks (optional)
59
59
  openInOffice, onRefreshAfterAddDcmtToFavs, onRefreshSearchAsyncDatagrid, onSelectedTIDChanged, onWFOperationCompleted, onClose, onFileOpened, onTaskCreateRequest, openWGsCopyMoveForm, openCommentFormCallback, openAddDocumentForm, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, passToArchiveCallback, onReferenceClick,
60
60
  // Tasks
61
61
  allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback,
62
62
  // Navigation
63
- handleNavigateToWGs, handleNavigateToDossiers, }) => {
63
+ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, }) => {
64
64
  // Ref for the floating bar container (used to position the floating action buttons)
65
65
  const floatingBarContainerRef = useRef(null);
66
+ // Ref per tracciare se autoFocusFirstRow=false deve bloccare l'auto-focus.
67
+ // Resta true finché l'utente non clicca manualmente sulla griglia per selezionare una riga.
68
+ // Quando l'utente seleziona manualmente, il ref viene settato a false e il focus automatico
69
+ // torna al comportamento normale.
70
+ const isFirstAutoFocusRef = useRef(true);
66
71
  // ID state to force remount of components
67
72
  const [id, setID] = useState('');
68
73
  // Document type descriptor of the currently document
@@ -139,6 +144,12 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
139
144
  }
140
145
  }
141
146
  };
147
+ // Handler unico per tutte le azioni utente che cambiano il focus (click, navigazione, etc.).
148
+ // Resetta isFirstAutoFocusRef perché da questo momento autoFocusFirstRow non deve più essere considerato.
149
+ const handleFocusedItemChangedFromGrid = useCallback((item) => {
150
+ isFirstAutoFocusRef.current = false;
151
+ setFocusedItem(item);
152
+ }, []);
142
153
  const onNavigateHandler = (dir) => {
143
154
  let index = -1;
144
155
  if (visibleItems && focusedItem) {
@@ -146,10 +157,10 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
146
157
  }
147
158
  switch (dir) {
148
159
  case 'next':
149
- setFocusedItem(visibleItems[index + 1]);
160
+ handleFocusedItemChangedFromGrid(visibleItems[index + 1]);
150
161
  break;
151
162
  default:
152
- setFocusedItem(visibleItems[index - 1]);
163
+ handleFocusedItemChangedFromGrid(visibleItems[index - 1]);
153
164
  break;
154
165
  }
155
166
  };
@@ -300,6 +311,7 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
300
311
  currentSearchResults,
301
312
  currentMetadataValues,
302
313
  allUsers,
314
+ fetchRemoteCertificates,
303
315
  searchResult: selectedSearchResult,
304
316
  datagridUtility: {
305
317
  visibleItems,
@@ -378,6 +390,9 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
378
390
  updateBatchUpdateForm(false);
379
391
  closeDcmtFormHandler();
380
392
  setCurrentSearchResults(searchResults);
393
+ // Resetta il ref quando arrivano nuovi risultati di ricerca,
394
+ // così la logica autoFocusFirstRow=false si riapplica per ogni nuova ricerca
395
+ isFirstAutoFocusRef.current = true;
381
396
  if (searchResults.length <= 0) {
382
397
  setSelectedSearchResult(undefined);
383
398
  return;
@@ -413,6 +428,12 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
413
428
  });
414
429
  checkRelatedDcmtsArchiveCapability();
415
430
  checkManyToManyCapability();
431
+ // se autoFocusFirstRow è false, non selezionare automaticamente la prima riga del nuovo risultato della ricerca
432
+ // Il ref resta true finché l'utente non clicca manualmente sulla griglia
433
+ if (autoFocusFirstRow === false && isFirstAutoFocusRef.current) {
434
+ setFocusedItem(undefined);
435
+ return;
436
+ }
416
437
  // Ricava il nuovo dataSource
417
438
  const newDataSource = searchResultDescriptorToSimpleArray(selectedSearchResult);
418
439
  // Se esiste almeno una riga, seleziona la prima
@@ -420,6 +441,12 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
420
441
  }, [selectedSearchResult, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability]);
421
442
  // Quando inputDID è fornito e i dati della griglia sono pronti, seleziona il documento corrispondente
422
443
  useEffect(() => {
444
+ // Se autoFocusFirstRow è false, non selezionare automaticamente la riga corrispondente a inputDID
445
+ // Il ref resta true finché l'utente non clicca manualmente sulla griglia
446
+ if (autoFocusFirstRow === false && isFirstAutoFocusRef.current) {
447
+ setFocusedItem(undefined);
448
+ return;
449
+ }
423
450
  if (!inputDID || visibleItems.length === 0)
424
451
  return;
425
452
  const targetItem = visibleItems.find((item) => item.DID == inputDID);
@@ -467,8 +494,10 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
467
494
  onOpenS4TViewerRequest?.([{ TID: focusedItem.TID, DID: focusedItem.DID }]);
468
495
  }, [openS4TViewer, inputDID, focusedItem, fromDTD]);
469
496
  useEffect(() => {
470
- if (!focusedItem)
497
+ if (!focusedItem) {
498
+ setCurrentDcmt(undefined);
471
499
  return;
500
+ }
472
501
  if (!fromDTD)
473
502
  return;
474
503
  let dtd = searchResults.length > 1 ? searchResults.find(res => res.fromTID == focusedItem?.TID)?.dtdResult : searchResults[0]?.dtdResult;
@@ -635,7 +664,7 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
635
664
  _jsxs(_Fragment, { children: [_jsxs(TMLayoutItem, { height: '100%', children: [_jsxs("div", { ref: floatingBarContainerRef, style: { position: 'relative', height: '100%', width: '100%' }, children: [_jsxs(TMSplitterLayout, { direction: 'horizontal', overflow: 'visible', separatorSize: Gutters.getGutters(), separatorActiveColor: 'transparent', separatorColor: 'transparent', min: ['0', '0'], showSeparator: showSelector && deviceType !== DeviceType.MOBILE, start: showSelector ? deviceType !== DeviceType.MOBILE ? ['30%', '70%'] : splitterSize : ['0%', '100%'], children: [showSelector ?
636
665
  _jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile || currentSearchResults.length === 1, onSelectionChanged: onSearchResultSelectionChanged }) })
637
666
  :
638
- _jsx(_Fragment, {}), _jsx(TMLayoutItem, { children: _jsx(TMSearchResultGrid, { openInOffice: openInOffice, fromDTD: fromDTD, operationItems: operationItems, allUsers: allUsers, inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, showExportForm: showExportForm, onFocusedItemChanged: setFocusedItem, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync({ inputDcmts, downloadType, downloadMode, onFileDownloaded: onFileOpened, confirmAttachments }), lastUpdateSearchTime: lastUpdateSearchTime, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], onSelectionChanged: (items) => { setSelectedItems(items); }, onDblClick: () => openFormHandler(LayoutModes.Update), showSearchTMDatagrid: showSearchTMDatagrid, onVisibleItemChanged: setVisibleItems, updateDataColumnsFromDataGrid: updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid: updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid: updateSelectedRowKeysFromDataGrid }) })] }), renderFloatingBar] }), _jsx(TMToppyDraggableHelpCenter, { usePortal: toppyHelpCenterUsePortal, isVisible: isToppyHelpCenterVisible, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { dtd: fromDTD, deviceType: deviceType, onApprove: () => {
667
+ _jsx(_Fragment, {}), _jsx(TMLayoutItem, { children: _jsx(TMSearchResultGrid, { openInOffice: openInOffice, fromDTD: fromDTD, operationItems: operationItems, allUsers: allUsers, inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, showExportForm: showExportForm, onFocusedItemChanged: handleFocusedItemChangedFromGrid, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync({ inputDcmts, downloadType, downloadMode, onFileDownloaded: onFileOpened, confirmAttachments }), lastUpdateSearchTime: lastUpdateSearchTime, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], onSelectionChanged: (items) => { setSelectedItems(items); }, onDblClick: () => openFormHandler(LayoutModes.Update), showSearchTMDatagrid: showSearchTMDatagrid, onVisibleItemChanged: setVisibleItems, updateDataColumnsFromDataGrid: updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid: updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid: updateSelectedRowKeysFromDataGrid, disableAutoFocus: autoFocusFirstRow === false && isFirstAutoFocusRef.current }) })] }), renderFloatingBar] }), _jsx(TMToppyDraggableHelpCenter, { usePortal: toppyHelpCenterUsePortal, isVisible: isToppyHelpCenterVisible, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { dtd: fromDTD, deviceType: deviceType, onApprove: () => {
639
668
  updateShowApprovePopup(true);
640
669
  }, onSignApprove: () => {
641
670
  handleSignApprove();
@@ -826,7 +855,7 @@ handleNavigateToWGs, handleNavigateToDossiers, }) => {
826
855
  _jsxs(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmSearchResult', children: [_jsx(PanelDisabledStateHandler, { isBoardDisabled: isBoardDisabled }), _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar })] }) }) }) }), renderDcmtOperations] }));
827
856
  };
828
857
  export default TMSearchResult;
829
- const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, inputFocusedItem, allowMultipleSelection = true, showExportForm = false, onFocusedItemChanged, onDownloadDcmtsAsync, onVisibleItemChanged, inputSelectedItems = [], lastUpdateSearchTime, searchResult, onSelectionChanged, onDblClick, showSearchTMDatagrid, updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid }) => {
858
+ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, inputFocusedItem, allowMultipleSelection = true, showExportForm = false, onFocusedItemChanged, onDownloadDcmtsAsync, onVisibleItemChanged, inputSelectedItems = [], lastUpdateSearchTime, searchResult, onSelectionChanged, onDblClick, showSearchTMDatagrid, updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid, disableAutoFocus = false }) => {
830
859
  const [dataSource, setDataSource] = useState();
831
860
  const [columns, setColumns] = useState([]);
832
861
  const [selectedRowKeys, setSelectedRowKeys] = useState([]);
@@ -850,6 +879,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
850
879
  const { loadDataListsAsync, renderDataListCell, dataListsCache } = useDataListItem();
851
880
  const { loadUsersAsync, renderUserIdViewer, usersCache } = useDataUserIdItem();
852
881
  useEffect(() => {
882
+ // Sincronizza focusedItem con inputFocusedItem dal padre
853
883
  if (deepCompare(inputFocusedItem, focusedItem))
854
884
  return;
855
885
  setFocusedItem(inputFocusedItem);
@@ -1034,10 +1064,25 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1034
1064
  // Recupera gli elementi della DataList dalla cache
1035
1065
  const dataListItems = dataListsCache.current.get(dataListID);
1036
1066
  if (dataListItems && dataListItems.length > 0) {
1037
- // Crea il datasource per l'headerFilter mostrando il nome (text) ma filtrando per valore (value)
1067
+ // Raccoglie i valori unici presenti nei risultati di ricerca per questa colonna
1068
+ let valuesInResults;
1069
+ try {
1070
+ valuesInResults = new Set();
1071
+ searchResult?.dtdResult?.rows?.forEach(row => {
1072
+ const cellValue = row[index];
1073
+ if (cellValue != null) {
1074
+ valuesInResults.add(String(cellValue));
1075
+ }
1076
+ });
1077
+ }
1078
+ catch {
1079
+ // Fallback: mostra tutti i valori della DataList
1080
+ valuesInResults = undefined;
1081
+ }
1082
+ // Crea il datasource per l'headerFilter
1038
1083
  headerFilterConfig = {
1039
1084
  dataSource: dataListItems
1040
- .filter(item => item.name != null && item.value != null)
1085
+ .filter(item => item.name != null && item.value != null && (valuesInResults === undefined || valuesInResults.has(String(item.value))))
1041
1086
  .map(item => ({
1042
1087
  text: item.name,
1043
1088
  value: item.value
@@ -1050,10 +1095,28 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1050
1095
  // Recupera tutti gli utenti dalla cache
1051
1096
  const users = Array.from(usersCache.current.values());
1052
1097
  if (users.length > 0) {
1098
+ // Raccoglie gli ID utente presenti nei risultati di ricerca per questa colonna
1099
+ let userIdsInResults;
1100
+ try {
1101
+ userIdsInResults = new Set();
1102
+ searchResult?.dtdResult?.rows?.forEach(row => {
1103
+ const cellValue = row[index];
1104
+ if (cellValue != null) {
1105
+ const userId = Number(cellValue);
1106
+ if (userId > 0) {
1107
+ userIdsInResults.add(userId);
1108
+ }
1109
+ }
1110
+ });
1111
+ }
1112
+ catch {
1113
+ // Fallback: mostra tutti gli utenti
1114
+ userIdsInResults = undefined;
1115
+ }
1053
1116
  // Crea il datasource per l'headerFilter mostrando nome utente completo (dominio\nome)
1054
1117
  headerFilterConfig = {
1055
1118
  dataSource: users
1056
- .filter(user => user.name != null && user.id != null)
1119
+ .filter(user => user.name != null && user.id != null && (userIdsInResults === undefined || userIdsInResults.has(user.id)))
1057
1120
  .map(user => ({
1058
1121
  text: user.domain ? `${user.domain}\\${user.name}` : user.name,
1059
1122
  value: user.id
@@ -1124,8 +1187,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1124
1187
  // Previene l'errore E1047 quando il filtro riferisce campi non più presenti nelle nuove colonne.
1125
1188
  const oldFields = columns.map(c => c.dataField);
1126
1189
  const newFields = cols.map(c => c.dataField);
1127
- const columnsChanged = oldFields.length !== newFields.length ||
1128
- oldFields.some((f, i) => f !== newFields[i]);
1190
+ const columnsChanged = oldFields.length !== newFields.length || oldFields.some((f, i) => f !== newFields[i]);
1129
1191
  if (columnsChanged) {
1130
1192
  const gridInstance = dataGridRef.current?.instance();
1131
1193
  if (gridInstance) {
@@ -1143,7 +1205,6 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1143
1205
  useEffect(() => {
1144
1206
  let newDataSource = searchResultDescriptorToSimpleArray(searchResult);
1145
1207
  setDataSource(newDataSource);
1146
- // setFocusedItem(newDataSource?.find(o => o?.rowIndex === focusedItem?.rowIndex));
1147
1208
  }, [lastUpdateSearchTime]);
1148
1209
  // Handles selection change in the data grid
1149
1210
  const handleSelectionChange = useCallback((e) => {
@@ -1154,14 +1215,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1154
1215
  }, [onSelectionChanged]);
1155
1216
  // Handles focus change in the data grid
1156
1217
  const handleFocusedRowChange = useCallback((e) => {
1157
- // if (setFocusedItem === undefined) return;
1158
- // if (e.row === undefined) { setFocusedItem(undefined); return; }
1159
- // setFocusedItem(e.row.data);
1218
+ // Non bloccare mai questo handler: è chiamato quando l'utente clicca su una riga
1160
1219
  onFocusedItemChanged?.(e.row?.data);
1161
- }, [setFocusedItem]);
1162
- // useEffect(() => {
1163
- // onFocusedItemChanged?.(focusedItem)
1164
- // }, [focusedItem])
1220
+ }, [onFocusedItemChanged]);
1165
1221
  // Handler for double-click row event
1166
1222
  const onRowDblClick = useCallback((e) => {
1167
1223
  if (onDblClick === undefined)
@@ -1182,7 +1238,6 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1182
1238
  });
1183
1239
  return;
1184
1240
  }
1185
- // setFocusedItem(e.data);
1186
1241
  onFocusedItemChanged?.(e.data);
1187
1242
  onDblClick();
1188
1243
  }, [onDblClick]);
@@ -1212,7 +1267,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1212
1267
  setVisibleItems(visibleData);
1213
1268
  }, []);
1214
1269
  useEffect(() => { onVisibleItemChanged?.(visibleItems); }, [visibleItems]);
1215
- return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [!isDataGridReady && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', gap: '10px' }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })), isDataGridReady && _jsx(TMDataGrid, { ref: dataGridRef, id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearchTMDatagrid, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: pageSize, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: operationItems, counterConfig: { show: true } })] });
1270
+ return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [!isDataGridReady && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', gap: '10px' }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })), isDataGridReady && _jsx(TMDataGrid, { ref: dataGridRef, id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: disableAutoFocus ? undefined : Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearchTMDatagrid, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: pageSize, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: operationItems, counterConfig: { show: true } })] });
1216
1271
  };
1217
1272
  //#region TMSearchResultSelector
1218
1273
  const StyledItemTemplate = styled.div `
@@ -0,0 +1,16 @@
1
+ import { DcmtTypeDescriptor, MetadataDescriptor, QueryDescriptor } from "@topconsultnpm/sdk-ts";
2
+ export type MetadataDescriptorWithKey = MetadataDescriptor & {
3
+ uniqueKey: string;
4
+ };
5
+ export declare function removeDuplicatesByTidMid<T extends {
6
+ tid?: number;
7
+ mid?: number;
8
+ }>(items: T[]): T[];
9
+ export declare function loadMetadataFromQd(qd: QueryDescriptor | undefined, filterMetadata?: (value: MetadataDescriptor, index: number, array: MetadataDescriptor[]) => unknown): Promise<{
10
+ metadata: MetadataDescriptorWithKey[];
11
+ dcmtTypes: DcmtTypeDescriptor[];
12
+ }>;
13
+ export declare function areArraysEqual<T extends {
14
+ tid?: number;
15
+ mid?: number;
16
+ }>(arr1: T[], arr2: T[], additionalCompare?: (a: T, b: T) => boolean): boolean;
@@ -0,0 +1,77 @@
1
+ import { DcmtTypeDescriptor, DcmtTypeListCacheService, SDK_Localizator } from "@topconsultnpm/sdk-ts";
2
+ import { getTIDsByQd } from "../../../helper/queryHelper";
3
+ import { SDKUI_Localizator } from "../../../helper";
4
+ import TMSpinner from "../../base/TMSpinner";
5
+ // =============================================================================
6
+ // HELPER: Rimuove duplicati dalla lista (basato su tid + mid)
7
+ // =============================================================================
8
+ export function removeDuplicatesByTidMid(items) {
9
+ const seen = new Set();
10
+ return items.filter(item => {
11
+ const key = `${item.tid}_${item.mid}`;
12
+ if (seen.has(key))
13
+ return false;
14
+ seen.add(key);
15
+ return true;
16
+ });
17
+ }
18
+ // =============================================================================
19
+ // FUNZIONE PRINCIPALE: Estrae i metadati dal QueryDescriptor
20
+ // =============================================================================
21
+ export async function loadMetadataFromQd(qd, filterMetadata) {
22
+ if (!qd)
23
+ return { metadata: [], dcmtTypes: [] };
24
+ const dtdList = [];
25
+ const metadata = [];
26
+ try {
27
+ const tidsFromQd = getTIDsByQd(qd);
28
+ const inputTIDs = tidsFromQd.map((item) => item.tid);
29
+ if (inputTIDs.length === 0)
30
+ return { metadata: [], dcmtTypes: [] };
31
+ TMSpinner.show({ description: `${SDKUI_Localizator.Loading} - ${SDK_Localizator.DcmtType} ...` });
32
+ const dtds = await DcmtTypeListCacheService.GetFromTIDsAsync(inputTIDs, true, undefined, false, false);
33
+ for (let i = 0; i < dtds.length; i++) {
34
+ const dtd = new DcmtTypeDescriptor();
35
+ dtd.init({ ...dtds[i] });
36
+ const alias = tidsFromQd?.[i]?.alias ?? '';
37
+ dtd.customData2 = alias;
38
+ dtd.metadata?.forEach((md) => {
39
+ const mdWithKey = md;
40
+ mdWithKey.customData1 = dtd.id;
41
+ mdWithKey.customData2 = alias;
42
+ mdWithKey.uniqueKey = `${dtd.id}_${md.id}_${alias}`;
43
+ });
44
+ const filteredMetadata = filterMetadata
45
+ ? dtd.metadata?.filter(filterMetadata)
46
+ : dtd.metadata;
47
+ dtdList.push({ ...dtd, metadata: filteredMetadata });
48
+ }
49
+ for (const dtd of dtdList) {
50
+ if (!dtd.metadata)
51
+ continue;
52
+ for (const md of dtd.metadata) {
53
+ metadata.push(md);
54
+ }
55
+ }
56
+ return { metadata, dcmtTypes: dtdList };
57
+ }
58
+ finally {
59
+ TMSpinner.hide();
60
+ }
61
+ }
62
+ // =============================================================================
63
+ // HELPER: Confronta due array per determinare se sono stati modificati
64
+ // =============================================================================
65
+ export function areArraysEqual(arr1, arr2, additionalCompare) {
66
+ if (arr1.length !== arr2.length)
67
+ return false;
68
+ for (let i = 0; i < arr1.length; i++) {
69
+ const a = arr1[i];
70
+ const b = arr2[i];
71
+ if (a.tid !== b.tid || a.mid !== b.mid)
72
+ return false;
73
+ if (additionalCompare && !additionalCompare(a, b))
74
+ return false;
75
+ }
76
+ return true;
77
+ }
@@ -33,7 +33,7 @@ import { TMColors } from "../../../utils/theme";
33
33
  import ShowAlert from "../../base/TMAlert";
34
34
  import TMButton from "../../base/TMButton";
35
35
  import { useDeviceType, DeviceType } from "../../base/TMDeviceProvider";
36
- import { TMExceptionBoxManager, TMMessageBoxManager, ButtonNames } from "../../base/TMPopUp";
36
+ import { TMExceptionBoxManager, TMMessageBoxManager, ButtonNames, sanitizeException } from "../../base/TMPopUp";
37
37
  import TMSpinner from "../../base/TMSpinner";
38
38
  import TMTooltip from "../../base/TMTooltip";
39
39
  import TMCheckBox from "../../editors/TMCheckBox";
@@ -457,6 +457,18 @@ const TMLoginForm = (props) => {
457
457
  // Skip auto-selection when defaultLoginValues provides an archiveId (external link)
458
458
  if (props.defaultLoginValues?.archiveId)
459
459
  return;
460
+ // Se dcmtArchive è già impostato (es. da rapid access), gestisci manualArchiveID
461
+ if (dcmtArchive) {
462
+ // Se la lista archivi è vuota, serve accesso manuale
463
+ if (archives.length === 0) {
464
+ setManualArchiveID(dcmtArchive.id ?? '');
465
+ }
466
+ else {
467
+ // Se l'archivio esiste nella lista, pulisci manualArchiveID
468
+ setManualArchiveID('');
469
+ }
470
+ return;
471
+ }
460
472
  if (archives.length === 1) {
461
473
  setDcmtArchive(archives[0]);
462
474
  return;
@@ -522,7 +534,7 @@ const TMLoginForm = (props) => {
522
534
  return true;
523
535
  }
524
536
  catch (e) {
525
- TMExceptionBoxManager.show({ exception: e });
537
+ TMExceptionBoxManager.show({ exception: sanitizeException(e) });
526
538
  return false;
527
539
  }
528
540
  finally {
@@ -558,7 +570,7 @@ const TMLoginForm = (props) => {
558
570
  }
559
571
  }
560
572
  catch (e) {
561
- TMExceptionBoxManager.show({ exception: e });
573
+ TMExceptionBoxManager.show({ exception: sanitizeException(e) });
562
574
  }
563
575
  finally {
564
576
  TMSpinner.hide();
@@ -10,6 +10,7 @@ interface TMWizardProps {
10
10
  initialData: any;
11
11
  title: string;
12
12
  description: string;
13
+ initialStep?: number;
13
14
  validateOnlyCurrentStep?: boolean;
14
15
  onClose: () => void;
15
16
  onFinish: (data: any) => void;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // TMWizard.tsx
3
- import React, { useState } from 'react';
3
+ import React, { useState, useEffect } from 'react';
4
4
  import styled from 'styled-components';
5
5
  import { ResultTypes } from '@topconsultnpm/sdk-ts';
6
6
  import TMButton from '../base/TMButton';
@@ -40,9 +40,11 @@ const ControlsContainer = styled.div `
40
40
  padding: 15px 20px;
41
41
  border-top: 1px solid #eee;
42
42
  `;
43
- const TMWizard = ({ steps, validateOnlyCurrentStep = true, onClose, onFinish, initialData, title, description }) => {
44
- const [currentStep, setCurrentStep] = useState(0);
43
+ const TMWizard = ({ steps, validateOnlyCurrentStep = true, onClose, onFinish, initialData, title, description, initialStep = 0 }) => {
44
+ const [currentStep, setCurrentStep] = useState(initialStep);
45
45
  const [formData, setFormData] = useState(initialData);
46
+ useEffect(() => { setCurrentStep(initialStep); }, [initialStep]);
47
+ useEffect(() => { setFormData(initialData); }, [initialData]);
46
48
  const stepValidationErrors = steps.map((step, index) => {
47
49
  if (validateOnlyCurrentStep) {
48
50
  if (index <= currentStep) {
@@ -23,11 +23,13 @@ export function LocalizeDcmtOperationTypes(value) {
23
23
  case DcmtOperationTypes.CheckFile: return SDKUI_Localizator.FileCheck;
24
24
  case DcmtOperationTypes.ConvertFile: return SDKUI_Localizator.FileConversion;
25
25
  case DcmtOperationTypes.VerifySign: return SDKUI_Localizator.VerifySignature;
26
+ case DcmtOperationTypes.CopyCompliant: return SDKUI_Localizator.CopyCompliant;
26
27
  case DcmtOperationTypes.SubstituteFile: return SDKUI_Localizator.AddOrSubstFile;
27
28
  case DcmtOperationTypes.FreeSearchReindex: return SDKUI_Localizator.IndexOrReindex;
28
29
  case DcmtOperationTypes.FreeSearchPurge: return SDKUI_Localizator.IndexingDelete;
29
30
  case DcmtOperationTypes.BatchUpdate: return SDKUI_Localizator.BatchUpdate;
30
31
  case DcmtOperationTypes.RemoveFromWorkgroup: return SDKUI_Localizator.RemoveFromWorkgroup;
32
+ case DcmtOperationTypes.ImportCertificates: return SDKUI_Localizator.ImportCertificates;
31
33
  default: return value;
32
34
  }
33
35
  }
@@ -137,6 +137,7 @@ export declare class SDKUI_Localizator {
137
137
  static get CopyAndReplace(): string;
138
138
  static get CopyButRename(): string;
139
139
  static get CopyCheckoutPath(): string;
140
+ static get CopyCompliant(): string;
140
141
  static get CopyToArchived(): string;
141
142
  static get CopyToArchivedDocuments(): string;
142
143
  static get CopyToClipboard(): "Erfolgreich in die Zwischenablage kopiert" | "Copy in clipboard" | "Copiar en portapapeles" | "Copier dans le presse-papier" | "Copiar na área de transferência" | "Copia negli appunti";
@@ -210,6 +211,9 @@ export declare class SDKUI_Localizator {
210
211
  static get DocumentOpenedSuccessfully(): "Dokument erfolgreich geöffnet" | "Document opened successfully" | "Documento abierto con éxito" | "Document ouvert avec succès" | "Documento aberto com sucesso" | "Documento aperto con successo";
211
212
  static get Document(): "Dokument" | "Document" | "Documento";
212
213
  static get Documents(): string;
214
+ static get DocumentsNotAvailableOrNoCorrelations(): string;
215
+ static get NoMasterDocumentsAvailable(): string;
216
+ static get NoRelationsAvailable(): string;
213
217
  static get DocumentOperations(): string;
214
218
  static get DocumentTypeNameAndDID(): string;
215
219
  static get DocumentTypeNameAndCustomMetadata(): string;
@@ -389,6 +393,7 @@ export declare class SDKUI_Localizator {
389
393
  static get ID_Hide(): "Ausblenden ID" | "Hide ID" | "Ocultar ID" | "Masquer ID" | "Nascondi ID";
390
394
  static get ID_Show(): "ID anzeigen" | "Show ID" | "Mostrar ID" | "Afficher ID" | "Visualizza ID";
391
395
  static get Import(): "Importieren" | "Import" | "Importar" | "Importer" | "Importa";
396
+ static get ImportCertificates(): string;
392
397
  static get ImportExport(): "Importieren/Exportieren" | "Import/Export" | "Importar/Exportar" | "Importer/Exporter" | "Importa/Esporta";
393
398
  static get ImportMRN(): "Importieren MRN" | "Import MRN" | "Importar MRN" | "Importer MRN";
394
399
  static get ImportMRNNDayBack(): "Anzahl der Tage für die Importdatenwiederherstellung (MRN)" | "Number of days for import data recovery (MRN)" | "Número de días para la recuperación de datos de importación (MRN)" | "Nombre de jours pour la récupération des données d'importation (MRN)" | "Número de dias para recuperação de dados de importação (MRN)" | "Numero di giorni per il recupero dei dati di Import (MRN)";
@@ -419,6 +424,11 @@ export declare class SDKUI_Localizator {
419
424
  static get LoadingWorkGroups(): string;
420
425
  static get LoadingParticipants(): string;
421
426
  static get Login(): string;
427
+ static get LoginAuth_ImplicitPINWithOTP(): string;
428
+ static get LoginAuth_ImplicitPINWithoutOTP(): string;
429
+ static get LoginAuth_PINWithOTP(): string;
430
+ static get LoginAuth_PINWithoutOTP(): string;
431
+ static get LoginAuth_Unknown(): string;
422
432
  static get LogDelete(): "Löschen der Logik" | "Logical delete" | "Cancelación lógica" | "Suppression logique" | "Lógica de cancelamento" | "Cancellazione logica";
423
433
  static get Logout(): "Abmelden" | "Logout" | "Cerrar sesión" | "Déconnexion" | "Sair" | "Esci";
424
434
  static get LogFolder(): "Protokollordner" | "Log folder" | "Carpeta de registro" | "Dossier de journal" | "Pasta de registro" | "Cartella di log";
@@ -487,6 +497,8 @@ export declare class SDKUI_Localizator {
487
497
  static get NoDataToDisplay(): "Keine Daten zum Anzeigen" | "No data to display" | "No hay datos para mostrar" | "Aucune donnée à afficher" | "Sem dados para exibir" | "Nessun dato da visualizzare";
488
498
  static get NoDcmtFound(): "Kein Dokument gefunden" | "No documents found" | "Ningún documento encontrado" | "Pas de documents trouvés" | "Nenhum documento encontrado" | "Nessun documento trovato";
489
499
  static get NoDcmtSelected(): string;
500
+ static get NoSortingApplied(): string;
501
+ static get NoOutputMetadata(): string;
490
502
  static get NoDetailDocumentFoundForArchiving(): "Kein Detaildokument zur Archivierung gefunden." | "No detail document found for archiving." | "No se encontró ningún documento de detalle para archivar." | "Aucun document détail trouvé pour l'archivage." | "Nenhum documento de detalhe encontrado para arquivamento." | "Nessun documento di dettaglio trovato per l'archiviazione.";
491
503
  static get NoDocumentMatchFound(): "Kein zugeordnetes Dokument gefunden." | "No document match found." | "No se encontró ningún documento emparejado." | "Aucun document associé trouvé." | "Nenhum documento correspondente encontrado." | "Nessun documento abbinato trovato.";
492
504
  static get NoDocumentSelectedForManyToManyMatching(): "Kein Dokument für die Mehrfach-Zuordnung ausgewählt." | "No document selected for many-to-many matching." | "Ningún documento seleccionado para el emparejamiento de muchos a muchos." | "Aucun document sélectionné pour l'association plusieurs à plusieurs." | "Nenhum documento selecionado para correspondência de muitos para muitos." | "Nessun documento selezionato per l'abbinamento molti a molti.";
@@ -680,6 +692,7 @@ export declare class SDKUI_Localizator {
680
692
  static get Seconds(): "Sekunden" | "Seconds" | "Segundos" | "Secondes" | "Segundas" | "Secondi";
681
693
  static get Select(): "Wählen Sie Ihre" | "Select" | "Seleccionar" | "Sélectionne" | "Selecione" | "Seleziona";
682
694
  static get SelectAnOperationBetween(): "Wählen Sie eine Operation zwischen" | "Select an operation between" | "Selecciona una operación entre" | "Sélectionnez une opération entre" | "Selecione uma operação entre" | "Seleziona un'operazione tra";
695
+ static get SelectDocumentToViewSearchResults(): "Wählen Sie ein Dokument aus, um die Details anzuzeigen" | "Select a document to view details" | "Selecciona un documento para ver los detalles" | "Sélectionnez un document pour afficher les détails" | "Selecione um documento para visualizar os detalhes" | "Seleziona un documento per visualizzare i dettagli";
683
696
  static get SelectArchiveToStart(): "Klicken Sie auf Archivieren, um zu beginnen." | "Click on Archive button to start." | "Haz clic en el botón Archivar para comenzar." | "Cliquez sur le bouton Archiver pour commencer." | "Clique no botão Arquivar para iniciar." | "Clicca sul pulsante Archivia per iniziare.";
684
697
  static get SelectAttachToStart(): "Sie können Dateien zu Ihrer E-Mail hinzufügen, klicken Sie auf Anhängen, um zu beginnen." | "You can attach files to your email, click on Attach button to start." | "Puedes adjuntar archivos a tu correo electrónico, haz clic en el botón Adjuntar para comenzar." | "Vous pouvez joindre des fichiers à votre e-mail, cliquez sur le bouton Joindre pour commencer." | "Você pode anexar arquivos ao seu e-mail, clique no botão Anexar para iniciar." | "Puoi allegare file alla tua email, clicca sul pulsante Allega per iniziare.";
685
698
  static get SelectFromAttachments(): "Aus Anhängen auswählen" | "Select from attachments" | "Seleccionar de archivos adjuntos" | "Sélectionner parmi les pièces jointes" | "Selecionar dos anexos" | "Seleziona dagli allegati";
@@ -738,6 +751,7 @@ export declare class SDKUI_Localizator {
738
751
  static get SignatureType(): string;
739
752
  static get Size(): "Größe" | "Size" | "Dimensión" | "Dimension" | "Tamanho" | "Dimensione";
740
753
  static get SortBy(): "Sortieren nach" | "Sort by" | "Ordenar por" | "Trier par" | "Ordinamento";
754
+ static get SortByPlural(): "Sortierungen" | "Sortings" | "Ordenaciones" | "Tris" | "Ordenações" | "Ordinamenti";
741
755
  static get Source(): "Ursprung" | "Source" | "Origen" | "Origem" | "Origine";
742
756
  static get SpecialOperators(): "Spezielle Operatoren" | "Special operators" | "Operadores especiales" | "Opérateurs spéciaux" | "Os operadores especiais" | "Operatori speciali";
743
757
  static get StandardMode(): "Standardmodus" | "Standard mode" | "Modo estándar" | "Mode standard" | "Modo padrão" | "Modalità standard";