@topconsultnpm/sdkui-react 6.22.0-t1 → 6.22.0-t3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +3 -3
  2. package/lib/components/NewComponents/ContextMenu/hooks.d.ts +1 -1
  3. package/lib/components/NewComponents/ContextMenu/styles.d.ts +45 -43
  4. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +6 -6
  5. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +80 -73
  6. package/lib/components/base/Styled.d.ts +92 -76
  7. package/lib/components/base/Styled.js +7 -5
  8. package/lib/components/base/TMAccordionNew.js +7 -7
  9. package/lib/components/base/TMAlert.d.ts +32 -32
  10. package/lib/components/base/TMAlert.js +6 -8
  11. package/lib/components/base/TMAreaManager.js +53 -55
  12. package/lib/components/base/TMButton.d.ts +13 -0
  13. package/lib/components/base/TMButton.js +139 -7
  14. package/lib/components/base/TMClosableList.js +3 -3
  15. package/lib/components/base/TMCustomButton.js +40 -48
  16. package/lib/components/base/TMDataGrid.d.ts +4 -0
  17. package/lib/components/base/TMDataGrid.js +292 -12
  18. package/lib/components/base/TMDataGridExportForm.js +39 -30
  19. package/lib/components/base/TMDropDownMenu.js +1 -1
  20. package/lib/components/base/TMEditorBase.d.ts +2 -0
  21. package/lib/components/base/TMFileManager.js +5 -3
  22. package/lib/components/base/TMFileManagerDataGridView.js +12 -3
  23. package/lib/components/base/TMFileManagerThumbnailItems.js +20 -10
  24. package/lib/components/base/TMFileManagerUtils.d.ts +6 -6
  25. package/lib/components/base/TMFileManagerUtils.js +37 -1
  26. package/lib/components/base/TMLayout.d.ts +2 -2
  27. package/lib/components/base/TMLayout.js +3 -3
  28. package/lib/components/base/TMList.js +1 -1
  29. package/lib/components/base/TMListView.js +3 -3
  30. package/lib/components/base/TMModal.d.ts +11 -0
  31. package/lib/components/base/TMModal.js +47 -13
  32. package/lib/components/base/TMPanel.d.ts +6 -0
  33. package/lib/components/base/TMPanel.js +141 -28
  34. package/lib/components/base/TMPopUp.js +5 -2
  35. package/lib/components/base/TMResizableMenu.js +2 -2
  36. package/lib/components/base/TMRightSidebar.js +1 -1
  37. package/lib/components/base/TMShowAllOrMaxItemsButton.js +1 -1
  38. package/lib/components/base/TMSpinner.d.ts +2 -1
  39. package/lib/components/base/TMSpinner.js +9 -7
  40. package/lib/components/base/TMTab.js +2 -2
  41. package/lib/components/base/TMTabControl.d.ts +106 -0
  42. package/lib/components/base/TMTabControl.js +664 -0
  43. package/lib/components/base/TMToggleButton.js +2 -2
  44. package/lib/components/base/TMToolbarCard.js +3 -3
  45. package/lib/components/base/TMTooltip.d.ts +2 -1
  46. package/lib/components/base/TMTooltip.js +24 -8
  47. package/lib/components/base/TMTreeView.d.ts +13 -7
  48. package/lib/components/base/TMTreeView.js +143 -70
  49. package/lib/components/base/TMWaitPanel.js +3 -3
  50. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  51. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  52. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  53. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  54. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
  55. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  56. package/lib/components/base/waitPanel/index.d.ts +1 -0
  57. package/lib/components/base/waitPanel/index.js +1 -0
  58. package/lib/components/choosers/TMCultureIDPicker.js +2 -2
  59. package/lib/components/choosers/TMDataListItemChooser.js +1 -1
  60. package/lib/components/choosers/TMDataListItemPicker.js +2 -2
  61. package/lib/components/choosers/TMDcmtTypeChooser.js +5 -4
  62. package/lib/components/choosers/TMDiskChooser.js +1 -1
  63. package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
  64. package/lib/components/choosers/TMDistinctValues.js +157 -141
  65. package/lib/components/choosers/TMDynDataListItemChooser.js +1 -1
  66. package/lib/components/choosers/TMImageIDChooser.js +1 -1
  67. package/lib/components/choosers/TMMetadataChooser.js +3 -3
  68. package/lib/components/choosers/TMPathChooser.js +17 -34
  69. package/lib/components/choosers/TMQuickSearchInfo.d.ts +13 -0
  70. package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
  71. package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
  72. package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
  73. package/lib/components/choosers/TMRelationChooser.js +1 -1
  74. package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
  75. package/lib/components/choosers/TMSelectedValuesSummary.js +89 -0
  76. package/lib/components/choosers/TMUserChooser.js +1 -1
  77. package/lib/components/editors/TMCheckBox.js +1 -1
  78. package/lib/components/editors/TMDateBox.d.ts +16 -1
  79. package/lib/components/editors/TMDateBox.js +90 -22
  80. package/lib/components/editors/TMDropDown.d.ts +3 -0
  81. package/lib/components/editors/TMDropDown.js +197 -5
  82. package/lib/components/editors/TMEditorStyled.d.ts +21 -43
  83. package/lib/components/editors/TMFormulaEditor.js +1 -1
  84. package/lib/components/editors/TMHtmlContentDisplay.js +1 -1
  85. package/lib/components/editors/TMLocalizedTextBox.js +1 -1
  86. package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
  87. package/lib/components/editors/TMMetadataEditor.js +27 -23
  88. package/lib/components/editors/TMMetadataValues.js +6 -6
  89. package/lib/components/editors/TMRadioButton.js +2 -2
  90. package/lib/components/editors/TMSummary.js +3 -3
  91. package/lib/components/editors/TMTextArea.d.ts +1 -0
  92. package/lib/components/editors/TMTextArea.js +25 -17
  93. package/lib/components/editors/TMTextBox.d.ts +2 -0
  94. package/lib/components/editors/TMTextBox.js +32 -21
  95. package/lib/components/editors/TMTextExpression.js +1 -1
  96. package/lib/components/features/archive/TMArchive.js +24 -5
  97. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +29 -0
  98. package/lib/components/features/assistant/TMHelpOnLineChat.js +1384 -0
  99. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +4 -4
  100. package/lib/components/features/assistant/TMToppySpeechBubble.js +1 -1
  101. package/lib/components/features/assistant/dataset/Designer-Suggested-Questions.json +102 -0
  102. package/lib/components/features/assistant/dataset/Login-Suggested-Questions.json +32 -0
  103. package/lib/components/features/assistant/dataset/Surfer-Suggested-Questions.json +102 -0
  104. package/lib/components/features/documents/TMBatchUpdateForm.js +22 -9
  105. package/lib/components/features/documents/TMCopyToFolderForm.js +50 -42
  106. package/lib/components/features/documents/TMDcmtBlog.d.ts +3 -0
  107. package/lib/components/features/documents/TMDcmtBlog.js +4 -4
  108. package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
  109. package/lib/components/features/documents/TMDcmtForm.js +281 -113
  110. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
  111. package/lib/components/features/documents/TMDcmtFormActionButtons.js +79 -9
  112. package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
  113. package/lib/components/features/documents/TMDcmtIcon.js +8 -8
  114. package/lib/components/features/documents/TMDcmtPreview.d.ts +7 -5
  115. package/lib/components/features/documents/TMDcmtPreview.js +16 -10
  116. package/lib/components/features/documents/TMDcmtTasks.js +2 -9
  117. package/lib/components/features/documents/TMDownloadRelationViewerSection.js +1 -1
  118. package/lib/components/features/documents/TMFileUploader.js +7 -7
  119. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  120. package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -108
  121. package/lib/components/features/documents/TMMasterInfoFields.d.ts +20 -0
  122. package/lib/components/features/documents/TMMasterInfoFields.js +185 -0
  123. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +1 -2
  124. package/lib/components/features/documents/TMMergeToPdfForm.js +83 -56
  125. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +46 -0
  126. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +320 -0
  127. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +370 -0
  128. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +283 -0
  129. package/lib/components/features/documents/TMRelationViewer.d.ts +29 -9
  130. package/lib/components/features/documents/TMRelationViewer.js +310 -208
  131. package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +1 -1
  132. package/lib/components/features/documents/relationTreeState.d.ts +32 -0
  133. package/lib/components/features/documents/relationTreeState.js +89 -0
  134. package/lib/components/features/search/TMMetadataOutputForm.js +1 -1
  135. package/lib/components/features/search/TMMetadataSorterForm.js +1 -1
  136. package/lib/components/features/search/TMSavedQuerySelector.js +2 -2
  137. package/lib/components/features/search/TMSearch.d.ts +1 -1
  138. package/lib/components/features/search/TMSearch.js +172 -34
  139. package/lib/components/features/search/TMSearchQueryEditor.js +7 -5
  140. package/lib/components/features/search/TMSearchQueryPanel.d.ts +14 -4
  141. package/lib/components/features/search/TMSearchQueryPanel.js +153 -62
  142. package/lib/components/features/search/TMSearchResult.d.ts +21 -0
  143. package/lib/components/features/search/TMSearchResult.js +461 -152
  144. package/lib/components/features/search/TMSignatureInfoContent.js +1 -1
  145. package/lib/components/features/search/TMTreeSelector.js +130 -83
  146. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  147. package/lib/components/features/tasks/TMTaskForm.js +1 -1
  148. package/lib/components/features/tasks/TMTaskFormUtils.js +3 -3
  149. package/lib/components/features/tasks/TMTasksAgenda.js +1 -1
  150. package/lib/components/features/tasks/TMTasksCalendar.js +1 -1
  151. package/lib/components/features/tasks/TMTasksHeader.js +1 -1
  152. package/lib/components/features/tasks/TMTasksUtilsView.js +1 -1
  153. package/lib/components/features/tasks/TMTasksView.js +97 -102
  154. package/lib/components/features/wg/TMWGsCopyMoveForm.js +92 -92
  155. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
  156. package/lib/components/features/workflow/TMWorkflowPopup.js +9 -5
  157. package/lib/components/features/workflow/diagram/ConnectionComponent.js +3 -3
  158. package/lib/components/features/workflow/diagram/DiagramItemComponent.js +3 -3
  159. package/lib/components/features/workflow/diagram/DiagramItemForm.js +1 -1
  160. package/lib/components/features/workflow/diagram/DiagramItemSvgContent.js +1 -1
  161. package/lib/components/features/workflow/diagram/RecipientList.js +1 -1
  162. package/lib/components/features/workflow/diagram/WFDiagram.js +4 -4
  163. package/lib/components/features/workflow/diagram/WorkitemRecipientsEditor.d.ts +3 -1
  164. package/lib/components/forms/Login/ChangePasswordInputs.d.ts +3 -1
  165. package/lib/components/forms/Login/Menu.js +1 -1
  166. package/lib/components/forms/Login/OTPReader.js +1 -1
  167. package/lib/components/forms/Login/PasswordStrengthChecker.js +1 -1
  168. package/lib/components/forms/Login/RapidAccessLogin.js +2 -2
  169. package/lib/components/forms/Login/RecoverPasswordFlow.js +2 -2
  170. package/lib/components/forms/Login/SelectBox.js +1 -1
  171. package/lib/components/forms/Login/StepIndicator.js +1 -1
  172. package/lib/components/forms/Login/TMLoginForm.js +7 -7
  173. package/lib/components/forms/Login/TextBox.js +1 -1
  174. package/lib/components/forms/TMApplyForm.js +3 -3
  175. package/lib/components/forms/TMChooserForm.js +1 -1
  176. package/lib/components/forms/TMResultDialog.js +1 -1
  177. package/lib/components/forms/TMSaveForm.js +3 -3
  178. package/lib/components/grids/TMBlogAttachments.js +14 -3
  179. package/lib/components/grids/TMBlogHeader.js +5 -1
  180. package/lib/components/grids/TMBlogsPost.d.ts +2 -0
  181. package/lib/components/grids/TMBlogsPost.js +122 -99
  182. package/lib/components/grids/TMBlogsPostUtils.d.ts +1 -0
  183. package/lib/components/grids/TMBlogsPostUtils.js +15 -4
  184. package/lib/components/grids/TMRecentsManager.js +4 -4
  185. package/lib/components/grids/TMValidationItemsList.js +1 -1
  186. package/lib/components/index.d.ts +6 -0
  187. package/lib/components/index.js +5 -0
  188. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  189. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +25 -15
  190. package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
  191. package/lib/components/layout/panelManager/TMPanelManagerToolbar.d.ts +4 -5
  192. package/lib/components/layout/panelManager/TMPanelManagerToolbar.js +1 -1
  193. package/lib/components/layout/panelManager/TMPanelWrapper.js +3 -3
  194. package/lib/components/layout/panelManager/types.d.ts +3 -0
  195. package/lib/components/layout/panelManager/utils.js +13 -11
  196. package/lib/components/pages/TMPage.js +30 -32
  197. package/lib/components/query/TMQueryCountButton.js +1 -1
  198. package/lib/components/query/TMQueryEditor.d.ts +17 -10
  199. package/lib/components/query/TMQueryEditor.js +9 -8
  200. package/lib/components/sidebar/TMCommandsPanel.d.ts +4 -4
  201. package/lib/components/sidebar/TMCommandsPanel.js +1 -1
  202. package/lib/components/sidebar/TMHeader.d.ts +1 -0
  203. package/lib/components/sidebar/TMHeader.js +37 -27
  204. package/lib/components/sidebar/TMSidebar.js +1 -1
  205. package/lib/components/sidebar/TMSidebarItem.js +1 -1
  206. package/lib/components/viewers/TMMidViewer.js +6 -3
  207. package/lib/components/viewers/TMTidViewer.js +89 -28
  208. package/lib/components/wizard/TMStepIndicator.js +3 -3
  209. package/lib/css/tm-sdkui.css +1 -1
  210. package/lib/devextreme-license.d.ts +1 -0
  211. package/lib/devextreme-license.js +3 -0
  212. package/lib/helper/Enum_Localizator.d.ts +4 -4
  213. package/lib/helper/Enum_Localizator.js +1 -0
  214. package/lib/helper/Globalization.d.ts +1 -1
  215. package/lib/helper/SDKUI_Globals.d.ts +110 -0
  216. package/lib/helper/SDKUI_Globals.js +151 -0
  217. package/lib/helper/SDKUI_Localizator.d.ts +756 -677
  218. package/lib/helper/SDKUI_Localizator.js +790 -0
  219. package/lib/helper/TMAppUpdatedModal.d.ts +2 -0
  220. package/lib/helper/TMAppUpdatedModal.js +134 -0
  221. package/lib/helper/TMIcons.d.ts +1 -0
  222. package/lib/helper/TMIcons.js +4 -3
  223. package/lib/helper/TMPdfViewer.js +1 -1
  224. package/lib/helper/TMToppyMessage.js +1 -1
  225. package/lib/helper/TMUtils.d.ts +51 -4
  226. package/lib/helper/TMUtils.js +273 -12
  227. package/lib/helper/ZipManager.d.ts +1 -1
  228. package/lib/helper/ZipManager.js +2 -1
  229. package/lib/helper/checkinCheckoutManager.d.ts +1 -1
  230. package/lib/helper/checkinCheckoutManager.js +1 -1
  231. package/lib/helper/dataGridSearchHelper.d.ts +28 -0
  232. package/lib/helper/dataGridSearchHelper.js +51 -0
  233. package/lib/helper/dcmtsHelper.d.ts +22 -0
  234. package/lib/helper/dcmtsHelper.js +60 -1
  235. package/lib/helper/helpers.d.ts +24 -2
  236. package/lib/helper/helpers.js +59 -3
  237. package/lib/helper/index.d.ts +2 -0
  238. package/lib/helper/index.js +2 -0
  239. package/lib/helper/queryHelper.d.ts +6 -1
  240. package/lib/helper/queryHelper.js +8 -3
  241. package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
  242. package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
  243. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  244. package/lib/hooks/useArchiveListForm.js +46 -0
  245. package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
  246. package/lib/hooks/useCaseFlowApprove.js +157 -0
  247. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  248. package/lib/hooks/useCheckInOutOperations.js +14 -34
  249. package/lib/hooks/useDataUserIdItem.d.ts +1 -0
  250. package/lib/hooks/useDataUserIdItem.js +1 -0
  251. package/lib/hooks/useDcmtOperations.d.ts +3 -12
  252. package/lib/hooks/useDcmtOperations.js +187 -179
  253. package/lib/hooks/useDocumentOperations.d.ts +5 -0
  254. package/lib/hooks/useDocumentOperations.js +64 -33
  255. package/lib/hooks/useInputDialog.js +1 -1
  256. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +98 -0
  257. package/lib/hooks/useMultiMasterDetailDcmts.js +758 -0
  258. package/lib/hooks/usePreventFileDrop.js +9 -0
  259. package/lib/hooks/useQueryParametersDialog.js +1 -1
  260. package/lib/hooks/useRelatedDocuments.d.ts +1 -0
  261. package/lib/hooks/useRelatedDocuments.js +22 -27
  262. package/lib/hooks/useShowDossiersV5.d.ts +12 -0
  263. package/lib/hooks/useShowDossiersV5.js +37 -0
  264. package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
  265. package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
  266. package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
  267. package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
  268. package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
  269. package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
  270. package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
  271. package/lib/hooks/useTMDistinctValuesSource.js +105 -0
  272. package/lib/hooks/useWorkflowApprove.js +33 -17
  273. package/lib/index.d.ts +2 -0
  274. package/lib/index.js +5 -2
  275. package/lib/services/caseflow/CaseFlowService.d.ts +277 -0
  276. package/lib/services/caseflow/CaseFlowService.js +761 -0
  277. package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
  278. package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
  279. package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
  280. package/lib/services/caseflow/caseFlowNameCache.js +116 -0
  281. package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
  282. package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
  283. package/lib/services/index.d.ts +4 -0
  284. package/lib/services/index.js +4 -0
  285. package/lib/services/platform_services.d.ts +4 -4
  286. package/package.json +16 -13
@@ -1,13 +1,13 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
3
3
  import styled from 'styled-components';
4
4
  import { LoadIndicator } from 'devextreme-react';
5
- import { AppModules, DataColumnTypes, DataListViewModes, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataDomains, MetadataFormats, SDK_Globals, SystemMIDsAsNumber, UserListCacheService, } from '@topconsultnpm/sdk-ts';
6
- import { deepCompare, generateUniqueColumnKeys, genUniqueId, getSearchToolbarVisibility, IconAll, IconBoard, IconDcmtTypeSys, IconDelete, IconMenuVertical, IconPlatform, IconRefresh, IconSearchCheck, IconShow, isApprovalWorkflowView, isSign4TopEnabled, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
5
+ import { AppModules, DataColumnTypes, DataListViewModes, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataDomains, SDK_Globals, SystemMIDsAsNumber, UserListCacheService, } from '@topconsultnpm/sdk-ts';
6
+ import { customizeSearchByDisplayText, deepCompare, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat, generateUniqueColumnKeys, genUniqueId, getColumnFormatInfo, getDateDisplayText, getNumberDisplayText, getSearchToolbarVisibility, IconAll, IconBoard, IconDcmtTypeSys, IconDelete, IconDuplicate, IconEdit, IconMenuVertical, IconPlatform, IconRefresh, IconSearchCheck, IconShow, isApprovalWorkflowView, isSign4TopEnabled, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, SDKUI_Globals, SDKUI_Localizator, IconCloseOutline, getSearchResultPanelLayout, getSearchResultPanelLayoutSignature, hasSearchResultPanelLayout, saveSearchResultPanelLayout, subscribeSearchResultPanelLayout } from '../../../helper';
7
7
  import { CounterItemKey } from '../../base/TMCounterContainer';
8
8
  import { getDcmtCicoStatus } from '../../../helper/checkinCheckoutManager';
9
9
  import { DcmtOperationTypes, SearchResultContext, } from '../../../ts';
10
- import { Gutters } from '../../../utils/theme';
10
+ import { Gutters, TMColors } from '../../../utils/theme';
11
11
  import { useDataListItem } from '../../../hooks/useDataListItem';
12
12
  import { useDataUserIdItem } from '../../../hooks/useDataUserIdItem';
13
13
  import { getSelectedDcmtsOrFocused, useDocumentOperations } from '../../../hooks/useDocumentOperations';
@@ -16,14 +16,14 @@ import TMAccordionNew from '../../base/TMAccordionNew';
16
16
  import TMButton from '../../base/TMButton';
17
17
  import TMDataGrid, { TMDataGridPageSize } from '../../base/TMDataGrid';
18
18
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
19
- import { TMLayoutItem, TMSplitterLayout } from '../../base/TMLayout';
19
+ import TMLayoutContainer, { TMLayoutItem, TMSplitterLayout } from '../../base/TMLayout';
20
20
  import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../../base/TMPopUp';
21
21
  import TMSpinner from '../../base/TMSpinner';
22
22
  import { StyledMultiViewPanel } from '../../base/Styled';
23
- import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
24
23
  import TMMetadataValues from '../../editors/TMMetadataValues';
25
24
  import TMTidViewer from '../../viewers/TMTidViewer';
26
- import { TMPanelManagerProvider, useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
25
+ import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
26
+ import { TMPanelManagerWithPersistenceProvider } from '../../layout/panelManager/TMPanelManagerWithPersistenceProvider';
27
27
  import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
28
28
  import TMContextMenu from '../../NewComponents/ContextMenu/TMContextMenu';
29
29
  import TMToppyDraggableHelpCenter from '../assistant/TMToppyDraggableHelpCenter';
@@ -33,7 +33,6 @@ import TMDcmtPreview from '../documents/TMDcmtPreview';
33
33
  import { WorkFlowOperationButtons } from '../workflow/TMWorkflowPopup';
34
34
  // Local
35
35
  import TMSearchResultFloatingActionButton from './TMSearchResultFloatingActionButton';
36
- let abortControllerLocal = new AbortController();
37
36
  //#region Helper Methods
38
37
  export const getSearchResultCountersSingleCategory = (searchResults) => {
39
38
  // let totDcmtTypes = searchResults.length;
@@ -51,13 +50,25 @@ const orderByName = (array) => {
51
50
  return 1;
52
51
  } return 0; });
53
52
  };
53
+ //#endregion Helper Methods
54
+ /**
55
+ * Contesti che ricordano il layout dei pannelli, condividendone uno solo.
56
+ * Gli altri sono esclusi di proposito: FREE_SEARCH ha un pannello in più, quindi il suo layout non vale
57
+ * per gli altri, e i restanti mostrano il risultato dentro contenitori con esigenze di spazio loro.
58
+ */
59
+ const PERSISTED_LAYOUT_CONTEXTS = [
60
+ SearchResultContext.METADATA_SEARCH,
61
+ SearchResultContext.WORKFLOW_APPROVE,
62
+ ];
54
63
  const TMSearchResult = ({
55
64
  // Data
56
65
  groupId, searchResults = [], context = SearchResultContext.METADATA_SEARCH, title, selectedSearchResultTID, floatingActionConfig, workingGroupContext = undefined, inputDID,
57
66
  // Boolean flags to enable/disable features
58
- 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,
67
+ autoFocusFirstRow = true, formAutoOpen, isVisible = true, allowRelations = true, openDcmtFormAsModal = false, showSearchResultSidebar = true, showDcmtFormSidebar = true, showSelector = false, isClosable = false, allowFloatingBar = true, showToolbarHeader = true, showBackButton = true, backCaption, showToolbarCloseButton = false, disableAccordionIfSingleCategory = false, editPdfForm = false, openS4TViewer = false, showTodoDcmtForm = false, showToppyDraggableHelpCenter = true, toppyHelpCenterUsePortal = false, showNoDcmtFoundMessage = true, enablePinIcons = true, showCounterConfig = true, showActionsColumn = false, compactPager = false, onRowDuplicateRequest,
68
+ // Counters
69
+ counterConfig,
59
70
  // Callbacks (optional)
60
- openInOffice, onRefreshAfterAddDcmtToFavs, onRefreshSearchAsyncDatagrid, onSelectedTIDChanged, onWFOperationCompleted, onClose, onFileOpened, onTaskCreateRequest, openWGsCopyMoveForm, openCommentFormCallback, openAddDocumentForm, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, passToArchiveCallback, onReferenceClick,
71
+ openInOffice, onRefreshAfterAddDcmtToFavs, onRefreshSearchAsyncDatagrid, onSelectedTIDChanged, onSelectedTIDCleared, onWFOperationCompleted, onClose, onFileOpened, onTaskCreateRequest, openWGsCopyMoveForm, openCommentFormCallback, openAddDocumentForm, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, passToArchiveCallback, onReferenceClick,
61
72
  // Tasks
62
73
  allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback,
63
74
  // Navigation
@@ -69,13 +80,19 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
69
80
  // Quando l'utente seleziona manualmente, il ref viene settato a false e il focus automatico
70
81
  // torna al comportamento normale.
71
82
  const isFirstAutoFocusRef = useRef(true);
83
+ // Mostra il pannello lista. È una ref perché il TMPanelManagerProvider sta dentro al JSX di
84
+ // questo componente, quindi qui il context non si può usare: la riempie ShowMainPanelBridge.
85
+ const showMainPanelRef = useRef(null);
72
86
  // ID state to force remount of components
73
87
  const [id, setID] = useState('');
74
88
  // Document type descriptor of the currently document
75
89
  const [fromDTD, setFromDTD] = useState();
76
90
  const [currentDcmt, setCurrentDcmt] = useState();
77
- // State to specify if the details form is open or not
78
- const [splitterSize, setSplitterSize] = useState(['100%', '0']);
91
+ // Passo corrente. Su mobile decide anche quale pannello è a schermo (vedi isSteppedLayout).
92
+ // 'auto' -> l'utente non ha ancora scelto: si può selezionare il primo tipo documento
93
+ // 'selector' -> è tornato indietro col back: non selezionare niente al posto suo
94
+ // 'results' -> è sulla griglia del tipo scelto
95
+ const [navStep, setNavStep] = useState('auto');
79
96
  // State to control the selected search result
80
97
  const [selectedSearchResult, setSelectedSearchResult] = useState();
81
98
  // State to control the search results to show in the datagrid
@@ -303,7 +320,16 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
303
320
  createTaskFromDocumentOrWorkItem();
304
321
  }
305
322
  };
306
- const { operationItems, renderFloatingBar, renderDcmtOperations, features, } = useDocumentOperations({
323
+ const handleRefreshClick = onRefreshSearchAsyncDatagrid ? async () => {
324
+ TMSpinner.show({ description: `${SDKUI_Localizator.UpdateInProgress}`, backgroundColor: 'transparent' });
325
+ try {
326
+ await onRefreshSearchAsyncDatagrid();
327
+ }
328
+ finally {
329
+ TMSpinner.hide();
330
+ }
331
+ } : undefined;
332
+ const { operationItems, selectedDcmtInfos, renderFloatingBar, renderDcmtOperations, features, } = useDocumentOperations({
307
333
  context: context,
308
334
  documentData: {
309
335
  dtd: fromDTD,
@@ -316,7 +342,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
316
342
  searchResult: selectedSearchResult,
317
343
  datagridUtility: {
318
344
  visibleItems,
319
- onRefreshSearchAsyncDatagrid,
345
+ onRefreshSearchAsyncDatagrid: handleRefreshClick,
320
346
  onRefreshDataRowsAsync,
321
347
  refreshFocusedDataRowAsync,
322
348
  onRefreshBlogDatagrid,
@@ -343,6 +369,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
343
369
  allowRelations,
344
370
  showTodoDcmtForm,
345
371
  graphometricManager,
372
+ showSelector,
346
373
  },
347
374
  tasks: {
348
375
  allTasks: allTasks,
@@ -377,9 +404,69 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
377
404
  openTaskFormHandler,
378
405
  },
379
406
  });
380
- const { isOpenDcmtForm, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid, showExportForm, isOpenBatchUpdate, isModifiedBatchUpdate, updateBatchUpdateForm, closeDcmtFormHandler, handleSignApprove, checkoutInfo: { showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, }, dcmtOperations: { abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
407
+ const { isOpenDcmtForm, isDcmtFormModal, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid, showDcmtTypesSelection, showExportForm, updateBatchUpdateForm, closeDcmtFormHandler, handleSignApprove, dcmtOperations: { downloadDcmtsAsync, runOperationAsync, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
408
+ // ----- Azioni della colonna di riga (showActionsColumn) -----------------------------------------
409
+ // Le voci di operationItems agiscono sui documenti selezionati o, in mancanza, su quello a fuoco:
410
+ // non accettano una riga come argomento. Il click sull'icona quindi azzera la selezione e porta il
411
+ // fuoco sulla riga cliccata, poi attende. L'attesa non è sul fuoco ma su selectedDcmtInfos, che
412
+ // l'hook ricalcola in un effect (un render più tardi): lanciare l'operazione prima significherebbe
413
+ // eseguirla sulla riga precedente.
414
+ const pendingRowActionRef = useRef(undefined);
415
+ // La callback è letta da una ref: dipendere dal prop renderebbe nuova requestRowAction, e con essa
416
+ // la colonna delle azioni, a ogni render del padre (vedi actionsColumn in TMSearchResultGrid)
417
+ const onRowDuplicateRequestRef = useRef(onRowDuplicateRequest);
418
+ onRowDuplicateRequestRef.current = onRowDuplicateRequest;
419
+ const requestRowAction = useCallback((operationId, rowData) => {
420
+ if (!showActionsColumn)
421
+ return;
422
+ if (rowData?.TID === undefined || rowData?.DID === undefined) {
423
+ ShowAlert({ message: SDKUI_Localizator.InvalidDcmt, mode: "warning", duration: 3000 });
424
+ return;
425
+ }
426
+ // Duplica ridefinita dal chiamante: agisce sulla riga cliccata, senza toccare fuoco e selezione
427
+ if (operationId === 'dup' && onRowDuplicateRequestRef.current) {
428
+ onRowDuplicateRequestRef.current(rowData);
429
+ return;
430
+ }
431
+ pendingRowActionRef.current = { operationId, TID: rowData.TID, DID: rowData.DID };
432
+ isFirstAutoFocusRef.current = false;
433
+ setSelectedItems([]);
434
+ setFocusedItem(rowData);
435
+ }, [showActionsColumn]);
436
+ // La richiesta viene scartata se il documento sparisce dai risultati (es. refresh dopo
437
+ // un'eliminazione): senza questo resterebbe appesa e partirebbe al prossimo cambio di fuoco.
438
+ useEffect(() => { pendingRowActionRef.current = undefined; }, [selectedSearchResult, lastUpdateSearchTime]);
439
+ useEffect(() => {
440
+ if (!showActionsColumn)
441
+ return;
442
+ const pending = pendingRowActionRef.current;
443
+ if (!pending)
444
+ return;
445
+ // L'hook non ha ancora recepito la riga richiesta: si riproverà al prossimo aggiornamento
446
+ if (selectedDcmtInfos.length !== 1)
447
+ return;
448
+ if (selectedDcmtInfos[0].TID !== pending.TID || selectedDcmtInfos[0].DID !== pending.DID)
449
+ return;
450
+ pendingRowActionRef.current = undefined;
451
+ const operation = findOperationItemById(operationItems, pending.operationId);
452
+ // Ora che il fuoco è sulla riga, `disabled` riflette i permessi per quel documento
453
+ if (!operation || operation.disabled || operation.visible === false)
454
+ return;
455
+ operation.onClick?.();
456
+ }, [showActionsColumn, selectedDcmtInfos, operationItems]);
381
457
  const deviceType = useDeviceType();
382
458
  const isMobile = deviceType === DeviceType.MOBILE;
459
+ const hasSingleDcmtType = currentSearchResults.length === 1;
460
+ // Su mobile si vede un pannello per volta; su desktop selettore e griglia sono affiancati
461
+ const isSteppedLayout = showSelector && isMobile;
462
+ // Un solo tipo documento su desktop: non c'è niente da scegliere, quindi il selettore sparisce
463
+ // (larghezza 0, nessun separatore) e lo spazio va tutto alla griglia. Il menu "Altro" permette
464
+ // di riaprirlo (vedi canToggleDcmtTypesSelection nell'hook).
465
+ const isSelectorCollapsed = showSelector && !isSteppedLayout && hasSingleDcmtType && !showDcmtTypesSelection;
466
+ // Senza selettore lo spazio va tutto alla griglia; altrimenti a step su mobile, affiancati su desktop
467
+ const splitterSizes = (!showSelector || isSelectorCollapsed) ? ['0%', '100%']
468
+ : isSteppedLayout ? (navStep === 'results' ? ['0', '100%'] : ['100%', '0'])
469
+ : ['30%', '70%'];
383
470
  const selectedDocs = getSelectedDcmtsOrFocused(selectedItems, focusedItem);
384
471
  // Disable the "Sign/Approve" button if:
385
472
  // 1. No document or multiple documents are selected, OR
@@ -400,8 +487,14 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
400
487
  setSelectedSearchResult(undefined);
401
488
  return;
402
489
  }
490
+ // Un solo tipo documento: selezionalo e vai ai risultati, a meno che l'utente sia tornato indietro
403
491
  if (searchResults.length === 1) {
492
+ if (navStep === 'selector') {
493
+ setSelectedSearchResult(undefined);
494
+ return;
495
+ }
404
496
  setSelectedSearchResult(searchResults[0]);
497
+ setNavStep('results');
405
498
  return;
406
499
  }
407
500
  // Se il TID precedentemente selezionato esiste ancora nei nuovi risultati, mantienilo.
@@ -415,6 +508,11 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
415
508
  return;
416
509
  }
417
510
  }
511
+ // L'utente è sul selettore
512
+ if (isMobile && navStep !== 'results') {
513
+ setSelectedSearchResult(undefined);
514
+ return;
515
+ }
418
516
  // Altrimenti seleziona il primo risultato ordinato
419
517
  setSelectedSearchResult(orderByName(searchResults)[0]);
420
518
  }, [searchResults]);
@@ -471,7 +569,9 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
471
569
  return;
472
570
  if (focusedItem?.DID != inputDID)
473
571
  return;
474
- openFormHandler(LayoutModes.Update);
572
+ if (focusedItem?.TID === undefined)
573
+ return;
574
+ onDcmtFormOpenChange(true, LayoutModes.Update);
475
575
  }, [inputDID, formAutoOpen, focusedItem]);
476
576
  // Quando openS4TViewer è true, apri automaticamente il viewer S4T
477
577
  useEffect(() => {
@@ -529,49 +629,77 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
529
629
  }, [currentMetadataValues]);
530
630
  const getTitleHeader = () => {
531
631
  let counters = (showSelector && disableAccordionIfSingleCategory && searchResults.length > 1) ? getSearchResultCountersSingleCategory(searchResults) : "";
632
+ // Nome del tipo documento selezionato, accodato al titolo
633
+ const selectedDcmtTypeName = selectedSearchResult?.fromName ?? (searchResults?.length === 1 ? searchResults[0]?.fromName : undefined);
634
+ const withSelectedDcmtTypeName = (baseTitle) => selectedDcmtTypeName ? `${baseTitle} - ${selectedDcmtTypeName}` : baseTitle;
532
635
  if (title)
533
- return title + counters;
534
- let titleHeader = 'Ricerca per metadati';
636
+ return withSelectedDcmtTypeName(title) + counters;
637
+ let titleHeader = withSelectedDcmtTypeName('Ricerca per metadati');
535
638
  switch (context) {
536
639
  case SearchResultContext.MASTER_DETAIL:
537
640
  case SearchResultContext.METADATA_SEARCH:
538
641
  titleHeader = `${searchResults?.length > 1 ? selectedSearchResult?.fromName ?? 'Ricerca per metadati' : searchResults[0]?.fromName ?? 'Ricerca per metadati'}`;
539
642
  break;
540
643
  case SearchResultContext.FAVORITES_AND_RECENTS:
541
- titleHeader = SDKUI_Localizator.FavoritesAndRecentDcmts;
644
+ titleHeader = withSelectedDcmtTypeName(SDKUI_Localizator.FavoritesAndRecentDcmts);
542
645
  break;
543
646
  case SearchResultContext.WORKFLOW_APPROVE:
544
- titleHeader = SDKUI_Localizator.WorkflowApproval;
647
+ titleHeader = withSelectedDcmtTypeName(SDKUI_Localizator.WorkflowApproval);
545
648
  break;
546
649
  case SearchResultContext.FREE_SEARCH:
547
- titleHeader = `Risultato della ricerca full text`;
650
+ titleHeader = withSelectedDcmtTypeName('Risultato della ricerca full text');
548
651
  break;
549
652
  }
550
653
  return titleHeader + counters;
551
654
  };
552
- const onBack = () => {
553
- if (isOpenBatchUpdate && isModifiedBatchUpdate) {
554
- TMMessageBoxManager.show({
555
- title: SDKUI_Localizator.Cancel, message: SDKUI_Localizator.ConfirmOnCancel, buttons: [ButtonNames.YES, ButtonNames.NO],
556
- onButtonClick: (e) => {
557
- if (e !== ButtonNames.YES)
558
- return;
559
- setSplitterSize(['100%', '0']);
560
- onClose?.();
561
- }
562
- });
563
- }
564
- else if (isOpenDcmtForm) {
565
- onDcmtFormOpenChange(false, LayoutModes.Update);
566
- }
567
- else {
568
- if (isMobile && currentSearchResults.length > 1) {
569
- setSelectedSearchResult(undefined);
570
- }
571
- setSplitterSize(['100%', '0']);
572
- onClose?.();
655
+ // Dai risultati si risale al selettore dei tipi documento
656
+ const canGoBackToSelector = isSteppedLayout && navStep === 'results' && selectedSearchResult !== undefined;
657
+ /**
658
+ * Back di tutti i pannelli (e X di chiusura se isClosable). Su mobile risale un livello alla volta:
659
+ * pannello secondario -> lista dei risultati -> selettore dei tipi documento -> uscita.
660
+ * Su desktop i pannelli sono affiancati, quindi resta solo l'uscita.
661
+ *
662
+ * @param fromSecondaryPanel - true se il back arriva da un pannello secondario
663
+ */
664
+ const onBack = useCallback((fromSecondaryPanel = false) => {
665
+ // Non è previsto nessun modo di uscire: non fare niente
666
+ if (!showBackButton && !isClosable)
667
+ return;
668
+ // Torna alla lista senza toccare gli stati: l'utente deve ritrovare la sua selezione
669
+ if (fromSecondaryPanel) {
670
+ showMainPanelRef.current?.();
671
+ return;
573
672
  }
574
- };
673
+ // Sia il ritorno al selettore sia l'uscita ripartono da una lista pulita
674
+ setSelectedSearchResult(undefined);
675
+ setFocusedItem(undefined);
676
+ setSelectedItems([]);
677
+ setSelectedRowKeysFromDataGrid([]);
678
+ setCurrentDcmt(undefined);
679
+ setCurrentMetadataValues([]);
680
+ setShowIndexingInfo(false);
681
+ isFirstAutoFocusRef.current = true;
682
+ // Il chiamante può tenere allineato il suo TID selezionato (es. selectedSearchResultTID)
683
+ onSelectedTIDCleared?.();
684
+ // [2] l'utente ha chiesto la lista: resta lì finché non sceglie lui, niente auto-selezione.
685
+ // [1] si esce: si riparte da capo, quindi l'auto-selezione torna permessa.
686
+ setNavStep(canGoBackToSelector ? 'selector' : 'auto');
687
+ // [2] C'è ancora un livello sopra: restiamo dentro, sul selettore
688
+ if (canGoBackToSelector)
689
+ return;
690
+ // [1] Si esce e si avvisa il chiamante
691
+ onClose?.();
692
+ }, [canGoBackToSelector, showBackButton, isClosable, onClose, onSelectedTIDCleared]);
693
+ /**
694
+ * Back del pannello lista. `undefined` = freccia non renderizzata, quindi la mostriamo solo se
695
+ * ha dove tornare: uno step intermedio, o un onClose per uscire. Con `isClosable` basta la X.
696
+ */
697
+ const backHandler = (showBackButton && !isClosable && (canGoBackToSelector || onClose !== undefined)) ? onBack : undefined;
698
+ /**
699
+ * Back dei pannelli secondari: la stessa onBack marcata come livello [3]. Solo su mobile:
700
+ * su desktop stanno affiancati alla lista e non c'è niente da cui tornare.
701
+ */
702
+ const backHandlerSecondary = useMemo(() => (isMobile && backHandler) ? () => backHandler(true) : undefined, [isMobile, backHandler]);
575
703
  const refreshDataGridAfterRemoveAsync = async () => {
576
704
  let index = selectedSearchResult?.dtdResult?.columns?.findIndex(col => col.caption === 'DID');
577
705
  let selectedRows = [];
@@ -603,21 +731,6 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
603
731
  return newResults;
604
732
  });
605
733
  };
606
- /**
607
- * Restituisce l'handler onBack per i pannelli, evitando duplicazione di codice.
608
- * @param isMainPanel - Se true, è il pannello principale dei risultati di ricerca
609
- */
610
- const getOnBackHandler = (isMainPanel = false) => {
611
- // Se showBackButton è false, non mostrare mai il back
612
- if (showBackButton === false)
613
- return undefined;
614
- // Mobile: mostra back su tutti i pannelli (se showBackButton è definito e non è closable)
615
- if (isMobile) {
616
- return (showBackButton !== undefined && !isClosable) ? onBack : undefined;
617
- }
618
- // Desktop: mostra back solo sul pannello principale dei risultati (se showBackButton è definito e non è closable)
619
- return (showBackButton !== undefined && isMainPanel && !isClosable) ? onBack : undefined;
620
- };
621
734
  const removeDcmtFromFavsOrRecents = async () => {
622
735
  switch (selectedSearchResult?.category) {
623
736
  case "Favorites":
@@ -634,9 +747,9 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
634
747
  };
635
748
  const onSearchResultSelectionChanged = (e) => {
636
749
  setSelectedSearchResult(e);
637
- if (deviceType === DeviceType.MOBILE) {
638
- setSplitterSize(['0', '100%']);
639
- }
750
+ // Scelto il tipo documento si passa ai risultati (a livello di layout è ignorato se i due
751
+ // step sono affiancati, ma vale comunque come "l'utente non è più fermo sul selettore")
752
+ setNavStep('results');
640
753
  onSelectedTIDChanged?.(e.fromTID);
641
754
  };
642
755
  const showToppyForApprove = useMemo(() => {
@@ -660,14 +773,15 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
660
773
  && !showTodoDcmtForm);
661
774
  }, [showToppyForApprove, showToppyDraggableHelpCenter, selectedDocs, showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, editPdfForm, openS4TViewer, showTodoDcmtForm]);
662
775
  const searchResutlToolbar = _jsxs(_Fragment, { children: [context === SearchResultContext.FAVORITES_AND_RECENTS &&
663
- _jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: onRefreshSearchAsyncDatagrid }), _jsx(TMContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) })] });
776
+ _jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: handleRefreshClick }), _jsx(TMContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) }), showToolbarCloseButton && onClose &&
777
+ _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconCloseOutline, { color: 'white' }), caption: SDKUI_Localizator.Close, onClick: onClose })] });
664
778
  const tmSearchResult = useMemo(() => (!searchResults || searchResults.length <= 0)
665
779
  ? _jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%' }, children: [_jsx(IconBoard, { fontSize: 96 }), showNoDcmtFoundMessage && _jsx("div", { style: { fontSize: "15px", marginTop: "10px" }, children: SDKUI_Localizator.NoDcmtFound }), openAddDocumentForm && _jsx("div", { style: { marginTop: "10px" }, children: _jsx(TMButton, { fontSize: "15px", icon: _jsx("i", { className: 'dx-icon-share' }), caption: SDKUI_Localizator.Share, onClick: openAddDocumentForm }) })] })
666
780
  :
667
- _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 ?
668
- _jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile || currentSearchResults.length === 1, onSelectionChanged: onSearchResultSelectionChanged }) })
781
+ _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: isSteppedLayout ? 'hidden' : 'visible', separatorSize: Gutters.getGutters(), separatorActiveColor: 'transparent', separatorColor: 'transparent', min: ['0', '0'], showSeparator: showSelector && !isSteppedLayout && !isSelectorCollapsed, start: splitterSizes, children: [showSelector ?
782
+ _jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile || hasSingleDcmtType, onSelectionChanged: onSearchResultSelectionChanged }) })
669
783
  :
670
- _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, dcmtsReturned: dcmtsReturned, dcmtsFound: dcmtsFound }) })] }), 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: () => {
784
+ _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, dcmtsReturned: dcmtsReturned, dcmtsFound: dcmtsFound, showCounterConfig: showCounterConfig, counterConfig: counterConfig, showActionsColumn: showActionsColumn, compactPager: compactPager, onRowActionRequest: requestRowAction }) })] }), 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: () => {
671
785
  updateShowApprovePopup(true);
672
786
  }, onSignApprove: () => {
673
787
  handleSignApprove();
@@ -687,7 +801,6 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
687
801
  showApprovePopup,
688
802
  showRejectPopup,
689
803
  showReAssignPopup,
690
- isOpenBatchUpdate,
691
804
  isOpenDetails,
692
805
  isOpenMaster,
693
806
  isOpenDcmtForm,
@@ -698,6 +811,10 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
698
811
  openS4TViewer,
699
812
  showBackButton,
700
813
  isMobile,
814
+ navStep,
815
+ isSteppedLayout,
816
+ isSelectorCollapsed,
817
+ currentSearchResults,
701
818
  currentMetadataValues,
702
819
  onRefreshSearchAsyncDatagrid,
703
820
  handleNavigateToWGs,
@@ -705,9 +822,14 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
705
822
  editPdfForm,
706
823
  showNoDcmtFoundMessage
707
824
  ]);
708
- const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: focusedItem?.TID, did: focusedItem?.DID, fetchBlogDataTrigger: refreshBlogTrigger, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), [focusedItem, allTasks, refreshBlogTrigger, handleNavigateToWGs, handleNavigateToDossiers, onRefreshBlogDatagrid]);
825
+ /**
826
+ * Il risultato è davvero a video: le pagine restano montate anche in secondo piano, e senza questo
827
+ * bacheca e anteprima scaricherebbero il documento fuori campo.
828
+ */
829
+ const isSearchResultOnScreen = isVisible && !isOpenDcmtForm && !isOpenDetails && !isOpenMaster;
830
+ const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: focusedItem?.TID, did: focusedItem?.DID, isPageVisible: isSearchResultOnScreen, fetchBlogDataTrigger: refreshBlogTrigger, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), [focusedItem, allTasks, refreshBlogTrigger, isSearchResultOnScreen, handleNavigateToWGs, handleNavigateToDossiers, onRefreshBlogDatagrid]);
709
831
  const tmSysMetadata = useMemo(() => _jsx(TMMetadataValues, { layoutMode: LayoutModes.Update, openChooserBySingleClick: true, TID: focusedItem?.TID, isReadOnly: true, deviceType: deviceType, metadataValues: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [] }), [focusedItem, currentMetadataValues, deviceType]);
710
- const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { refreshPreviewTrigger: refreshPreviewTrigger, currentDcmt: currentDcmt, onBack: getOnBackHandler() }), [currentDcmt, refreshPreviewTrigger]);
832
+ const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { refreshPreviewTrigger: refreshPreviewTrigger, currentDcmt: currentDcmt, isPageVisible: isSearchResultOnScreen, onBack: backHandlerSecondary }), [currentDcmt, refreshPreviewTrigger, isSearchResultOnScreen, backHandlerSecondary]);
711
833
  // Auto-fetch indexing info when drawer is open and focusedItem changes
712
834
  useEffect(() => {
713
835
  if (!focusedItem || !showIndexingInfo)
@@ -762,8 +884,24 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
762
884
  if (!ftExplanation) {
763
885
  return (_jsxs(StyledPlaceholder, { children: [" ", _jsx("p", { children: "Nessuna info disponibile per questo documento" }), " "] }));
764
886
  }
765
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', width: '100%' }, children: [_jsx("div", { style: { padding: '10px', overflow: 'auto', flex: 1 }, children: _jsx("div", { dangerouslySetInnerHTML: { __html: ftExplanation } }) }), _jsxs(StyledIndexingInfoSection, { children: [_jsxs(StyledIndexingToggle, { onClick: handleToggleIndexingInfo, disabled: loadingIndexingInfo, children: [_jsx(StyledLeftContent, { children: _jsx("span", { children: SDKUI_Localizator.IndexingInformation }) }), _jsx(StyledRightContent, { children: _jsx(StyledChevron, { "$isOpen": showIndexingInfo, children: "\u25BC" }) })] }), showIndexingInfo && indexingInfo && (_jsxs(StyledIndexingInfoBox, { children: [_jsx("div", { dangerouslySetInnerHTML: { __html: indexingInfo } }), loadingIndexingInfo && (_jsxs("div", { style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: 'rgba(255, 255, 255, 0.9)', padding: '10px', borderRadius: '4px', boxShadow: '0 2px 8px rgba(0,0,0,0.15)' }, children: [SDKUI_Localizator.Loading, "..."] }))] }))] })] }));
887
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', width: '100%' }, children: [_jsx("div", { style: { padding: '10px', overflow: 'auto', flex: 1 }, children: _jsx("div", { dangerouslySetInnerHTML: { __html: ftExplanation } }) }), _jsxs(StyledIndexingInfoSection, { children: [_jsxs(StyledIndexingToggle, { onClick: handleToggleIndexingInfo, disabled: loadingIndexingInfo, children: [_jsx(StyledLeftContent, { children: _jsx("span", { children: SDKUI_Localizator.IndexingInformation }) }), _jsx(StyledRightContent, { children: _jsx(StyledChevron, { $isOpen: showIndexingInfo, children: "\u25BC" }) })] }), showIndexingInfo && indexingInfo && (_jsxs(StyledIndexingInfoBox, { children: [_jsx("div", { dangerouslySetInnerHTML: { __html: indexingInfo } }), loadingIndexingInfo && (_jsxs("div", { style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: 'rgba(255, 255, 255, 0.9)', padding: '10px', borderRadius: '4px', boxShadow: '0 2px 8px rgba(0,0,0,0.15)' }, children: [SDKUI_Localizator.Loading, "..."] }))] }))] })] }));
766
888
  }, [selectedSearchResult, focusedItem, indexingInfo, showIndexingInfo, loadingIndexingInfo]);
889
+ /**
890
+ * Su mobile i pannelli si mostrano uno per volta, quindi il layout non va ricordato; con groupId
891
+ * i pannelli sono di un panel manager esterno, che ha un layout suo. Senza la barra laterale
892
+ * l'utente non può aprire/chiudere i pannelli: non ha senso applicargli il layout salvato (li
893
+ * troverebbe aperti senza poterli chiudere) né lasciargli sporcare quello condiviso.
894
+ * Il layout salvato vale solo per il login con Surfer: gli altri moduli hanno layout propri.
895
+ */
896
+ const isLayoutPersisted = SDK_Globals.tmSession?.SessionDescr?.appModuleID === AppModules.SURFER && !isMobile && !groupId?.length && showSearchResultSidebar && PERSISTED_LAYOUT_CONTEXTS.includes(context);
897
+ /**
898
+ * Pannelli che questo risultato non può mostrare (la bacheca è l'unico, vedi PanelDisabledStateHandler).
899
+ * Il layout è condiviso con gli altri risultati aperti: qui non vanno aperti quando il layout lo chiede,
900
+ * e il loro stato salvato non va toccato, altrimenti li si chiuderebbe dove invece sono attivi.
901
+ */
902
+ const disabledLayoutPanelIds = isBoardDisabled ? ['tmBlog'] : [];
903
+ /** Bacheca aperta nel layout salvato: da riaprire quando il tipo documento torna a prevederla */
904
+ const savedBlogVisible = isLayoutPersisted && (getSearchResultPanelLayout()['tmBlog']?.visible ?? false);
767
905
  const allInitialPanelVisibility = {
768
906
  'tmSearchResult': true,
769
907
  'tmBlog': false,
@@ -786,9 +924,11 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
786
924
  component: tmSearchResult,
787
925
  panelContainer: {
788
926
  title: getTitleHeader(),
927
+ enableTitleMarquee: true,
789
928
  showHeader: showToolbarHeader,
790
929
  allowMaximize: !isMobile,
791
- onBack: getOnBackHandler(true),
930
+ onBack: backHandler,
931
+ backCaption: backCaption,
792
932
  onClose: isClosable ? onBack : undefined,
793
933
  toolbar: searchResutlToolbar
794
934
  },
@@ -803,7 +943,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
803
943
  panelContainer: {
804
944
  title: SDKUI_Localizator.BlogCase,
805
945
  allowMaximize: !isMobile,
806
- onBack: getOnBackHandler(),
946
+ onBack: backHandlerSecondary,
807
947
  }
808
948
  },
809
949
  toolbarOptions: { icon: _jsx(IconBoard, { fontSize: 24 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmBlog, orderNumber: 2, isActive: allInitialPanelVisibility['tmBlog'] }
@@ -816,7 +956,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
816
956
  panelContainer: {
817
957
  title: SDKUI_Localizator.MetadataSystem,
818
958
  allowMaximize: !isMobile,
819
- onBack: getOnBackHandler(),
959
+ onBack: backHandlerSecondary,
820
960
  }
821
961
  },
822
962
  toolbarOptions: { icon: _jsx(IconDcmtTypeSys, { fontSize: 24 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmSysMetadata, orderNumber: 3, isActive: allInitialPanelVisibility['tmSysMetadata'] }
@@ -830,7 +970,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
830
970
  panelContainer: {
831
971
  title: SDKUI_Localizator.ResultDetails,
832
972
  allowMaximize: !isMobile,
833
- onBack: getOnBackHandler(),
973
+ onBack: backHandlerSecondary,
834
974
  }
835
975
  },
836
976
  toolbarOptions: { icon: _jsx(IconPlatform, { fontSize: 20 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmFullTextSearch, orderNumber: 4, isActive: allInitialPanelVisibility['tmFullTextSearch'] }
@@ -844,21 +984,49 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
844
984
  },
845
985
  toolbarOptions: { icon: _jsx(IconShow, { fontSize: 24 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmDcmtPreview, orderNumber: context === SearchResultContext.FREE_SEARCH ? 5 : 4, isActive: allInitialPanelVisibility['tmDcmtPreview'] }
846
986
  }
847
- ], [tmSearchResult, tmBlog, tmSysMetadata, tmDcmtPreview, tmFullTextSearch, showToolbarHeader, context, isMobile]);
848
- return (_jsxs(StyledMultiViewPanel, { "$isVisible": isVisible, children: [_jsx(StyledMultiViewPanel, { "$isVisible": !isOpenDcmtForm && !isOpenDetails && !isOpenMaster, style: {
987
+ ], [tmSearchResult, tmBlog, tmSysMetadata, tmDcmtPreview, tmFullTextSearch, showToolbarHeader, context, isMobile, backHandler, backHandlerSecondary, isClosable, onBack, backCaption]);
988
+ /** Contenuto del panel manager: con groupId sta nel provider del contenitore, altrimenti in quello qui sotto */
989
+ const panelManagerContent = (_jsxs(_Fragment, { children: [_jsx(PanelDisabledStateHandler, { isBoardDisabled: isBoardDisabled, savedBlogVisible: savedBlogVisible }), isLayoutPersisted && _jsx(SavedLayoutSyncHandler, { disabledPanelIds: disabledLayoutPanelIds }), _jsx(ShowMainPanelBridge, { showMainPanelRef: showMainPanelRef }), _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar })] }));
990
+ return (_jsxs(StyledMultiViewPanel, { $isVisible: isVisible, children: [_jsx(StyledMultiViewPanel, { $isVisible: (!isOpenDcmtForm || isDcmtFormModal) && !isOpenDetails && !isOpenMaster, style: {
849
991
  display: 'flex',
850
992
  flexDirection: isMobile ? 'column' : 'row',
851
993
  justifyContent: 'space-between',
852
994
  gap: Gutters.getGutters(),
853
995
  width: '100%',
854
996
  height: '100%',
855
- }, 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(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showCicoWaitPanel, showWaitPanelPrimary: showCicoPrimaryProgress, waitPanelTitle: cicoWaitPanelTitle, waitPanelTextPrimary: cicoPrimaryProgressText, waitPanelValuePrimary: cicoPrimaryProgressValue, waitPanelMaxValuePrimary: cicoPrimaryProgressMax, isCancelable: true, abortController: abortControllerLocal, children: (groupId && groupId.length > 0) ?
856
- _jsxs(_Fragment, { children: [_jsx(PanelDisabledStateHandler, { isBoardDisabled: isBoardDisabled }), _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar })] })
857
- :
858
- _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] }));
997
+ }, children: _jsx(TMLayoutContainer, { direction: 'vertical', children: (groupId && groupId.length > 0) ? panelManagerContent
998
+ // Senza le prop di persistenza il provider si comporta come quello base:
999
+ // i contesti che non ricordano il layout restano come prima
1000
+ : _jsx(TMPanelManagerWithPersistenceProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmSearchResult', isPersistenceEnabled: isLayoutPersisted && hasSearchResultPanelLayout(), persistPanelStates: isLayoutPersisted ? (state) => saveSearchResultPanelLayout(state, disabledLayoutPanelIds) : undefined, persistedPanelStates: isLayoutPersisted ? getSearchResultPanelLayout() : undefined, children: panelManagerContent }) }) }), renderDcmtOperations] }));
859
1001
  };
860
1002
  export default TMSearchResult;
861
- 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, dcmtsReturned, dcmtsFound }) => {
1003
+ /** Cerca una voce di operazione per id, anche dentro i sottomenu */
1004
+ const findOperationItemById = (items, id) => {
1005
+ for (const item of items) {
1006
+ if (item.id === id)
1007
+ return item;
1008
+ const found = item.submenu ? findOperationItemById(item.submenu, id) : undefined;
1009
+ if (found)
1010
+ return found;
1011
+ }
1012
+ return undefined;
1013
+ };
1014
+ /**
1015
+ * Azioni della colonna di riga. Gli id sono quelli di useDocumentOperations, così il comportamento è
1016
+ * lo stesso del menu contestuale; icone e colori sono allineati alle altre griglie mentre il tooltip
1017
+ * resta il nome dell'operazione. "Cancella" è la cancellazione logica: ripristino ed eliminazione
1018
+ * fisica restano nel menu contestuale, dove la scelta è esplicita.
1019
+ */
1020
+ const rowActions = [
1021
+ { operationId: 'open-form', icon: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) },
1022
+ { operationId: 'dup', icon: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) },
1023
+ { operationId: 'del-log', icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }), isVisible: (rowData) => rowData?.ISLOGDEL != 1 },
1024
+ ];
1025
+ const rowActionsStyles = {
1026
+ cell: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' },
1027
+ icon: { cursor: 'pointer', display: 'flex' },
1028
+ };
1029
+ 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, dcmtsReturned, dcmtsFound, showCounterConfig = true, counterConfig, showActionsColumn = false, compactPager = false, onRowActionRequest, }) => {
862
1030
  const [dataSource, setDataSource] = useState();
863
1031
  const [columns, setColumns] = useState([]);
864
1032
  const [selectedRowKeys, setSelectedRowKeys] = useState([]);
@@ -879,8 +1047,8 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
879
1047
  useEffect(() => {
880
1048
  updateDataSourceFromDataGrid(dataSource ?? []);
881
1049
  }, [dataSource]);
882
- const { loadDataListsAsync, renderDataListCell, dataListsCache } = useDataListItem();
883
- const { loadUsersAsync, renderUserIdViewer, usersCache } = useDataUserIdItem();
1050
+ const { loadDataListsAsync, renderDataListCell, dataListsCache, getDataListItem } = useDataListItem();
1051
+ const { loadUsersAsync, renderUserIdViewer, usersCache, getUserItem, getCompleteUserName } = useDataUserIdItem();
884
1052
  useEffect(() => {
885
1053
  // Sincronizza focusedItem con inputFocusedItem dal padre
886
1054
  if (deepCompare(inputFocusedItem, focusedItem))
@@ -1000,42 +1168,14 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1000
1168
  const getDisplayFormat = useCallback((col) => {
1001
1169
  if (col.dataType === DataColumnTypes.Text)
1002
1170
  return undefined;
1003
- const format = MetadataFormats[(col.extendedProperties?.["Format"] ?? "None")];
1004
- const formatCulture = col.extendedProperties?.["FormatCulture"] ?? window.navigator.language;
1005
- if (col.dataType === DataColumnTypes.DateTime) {
1006
- return {
1007
- formatter: function (value) {
1008
- switch (format) {
1009
- case MetadataFormats.ShortDate: return value.toLocaleString(formatCulture, formatCulture == "it-IT" ? { year: "numeric", month: "2-digit", day: "2-digit" } : { dateStyle: 'short' });
1010
- case MetadataFormats.ShortTime: return value.toLocaleString(formatCulture, { timeStyle: 'short' });
1011
- case MetadataFormats.ShortDateLongTime: return value.toLocaleString(formatCulture, formatCulture == "it-IT" ? { year: "numeric", month: "2-digit", day: "2-digit", hour: '2-digit', minute: '2-digit', second: '2-digit' } : { dateStyle: 'short', timeStyle: 'medium' }).replace(',', '');
1012
- case MetadataFormats.ShortDateShortTime: return value.toLocaleString(formatCulture, formatCulture == "it-IT" ? { year: "numeric", month: "2-digit", day: "2-digit", hour: '2-digit', minute: '2-digit' } : { dateStyle: 'short', timeStyle: 'short' }).replace(',', '');
1013
- case MetadataFormats.LongDate: return value.toLocaleString(formatCulture, { weekday: "long", year: "numeric", month: "long", day: "numeric" });
1014
- case MetadataFormats.LongTime: return value.toLocaleString(formatCulture, { timeStyle: 'medium' });
1015
- case MetadataFormats.LongDateLongTime: return value.toLocaleString(formatCulture, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: '2-digit', minute: '2-digit', second: '2-digit' });
1016
- case MetadataFormats.LongDateShortTime: return value.toLocaleString(formatCulture, { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: '2-digit', minute: '2-digit' });
1017
- default: return value.toLocaleString(formatCulture, formatCulture == "it-IT" ? { year: "numeric", month: "2-digit", day: "2-digit" } : { dateStyle: 'short' });
1018
- }
1019
- }
1020
- };
1021
- }
1022
- if (col.dataType === DataColumnTypes.Number) {
1023
- return {
1024
- formatter: function (value) {
1025
- return value.toLocaleString(formatCulture, { useGrouping: format == MetadataFormats.NumberWithThousandsSeparator });
1026
- }
1027
- };
1028
- }
1029
- if (format == MetadataFormats.None)
1030
- return undefined;
1031
- if (format == MetadataFormats.CurrencyEuro)
1032
- return { type: 'currency', precision: 2, currency: "EUR" };
1033
- if (format == MetadataFormats.CurrencyDollar)
1034
- return { type: 'currency', precision: 2, currency: "USD" };
1035
- if (format == MetadataFormats.CurrencyPound)
1036
- return { type: 'currency', precision: 2, currency: "GBP" };
1037
- if (format == MetadataFormats.CurrencyYen)
1038
- return { type: 'currency', currency: "JPY" };
1171
+ // Formato e cultura di visualizzazione definiti sulle proprietà estese della colonna
1172
+ const { format, formatCulture } = getColumnFormatInfo(col);
1173
+ // Date: la griglia passa al formatter un oggetto Date già istanziato
1174
+ if (col.dataType === DataColumnTypes.DateTime)
1175
+ return { formatter: (value) => formatDateTimeByMetadataFormat(value, format, formatCulture) };
1176
+ // Numeri (incluse valute): stessa formattazione usata da TMRelationViewer
1177
+ if (col.dataType === DataColumnTypes.Number)
1178
+ return { formatter: (value) => formatNumberByMetadataFormat(value, format, formatCulture) };
1039
1179
  return undefined;
1040
1180
  }, []);
1041
1181
  /**
@@ -1140,6 +1280,57 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1140
1280
  });
1141
1281
  return cols;
1142
1282
  }, [searchResult, dataType, cellRender, getDisplayFormat, dataListsCache, usersCache]);
1283
+ /**
1284
+ * Colonne in cui il testo mostrato è diverso dal valore archiviato: date e numeri formattati, descrizioni
1285
+ * di liste dati e utenti. Su queste la ricerca della griglia va fatta sul testo mostrato, altrimenti non
1286
+ * trova nulla (es. cercando 29/01/2023 su una data, che nei dati è una stringa ISO).
1287
+ * Le cache sono ref lette al momento della ricerca: a quel punto sono già popolate.
1288
+ */
1289
+ const searchTextGetters = useMemo(() => {
1290
+ const getters = new Map();
1291
+ const columnsOfResult = searchResult?.dtdResult?.columns;
1292
+ if (!columnsOfResult)
1293
+ return getters;
1294
+ const uniqueKeys = generateUniqueColumnKeys(columnsOfResult, searchResult?.fromTID);
1295
+ columnsOfResult.forEach((col, index) => {
1296
+ const dataField = uniqueKeys[index];
1297
+ if (!dataField)
1298
+ return;
1299
+ const dataDomain = MetadataDataDomains[(col.extendedProperties?.["DataDomain"] ?? "None")];
1300
+ const dataListID = Number(col.extendedProperties?.["DataListID"]);
1301
+ if (dataDomain === MetadataDataDomains.DataList && dataListID) {
1302
+ getters.set(dataField, (rowData) => getDataListItem(dataListID, rowData?.[dataField])?.name ?? String(rowData?.[dataField] ?? ''));
1303
+ return;
1304
+ }
1305
+ if (dataDomain === MetadataDataDomains.UserID) {
1306
+ getters.set(dataField, (rowData) => {
1307
+ const rawValue = rowData?.[dataField];
1308
+ // Un campo utente vuoto non mostra nulla: senza questo controllo Number('') darebbe 0, cioè l'utente di sistema
1309
+ if (rawValue === undefined || rawValue === null || rawValue === '')
1310
+ return '';
1311
+ const userId = Number(rawValue);
1312
+ if (Number.isNaN(userId))
1313
+ return String(rawValue);
1314
+ if (userId === 0)
1315
+ return SDKUI_Localizator.SystemUser ?? '';
1316
+ const ud = getUserItem(userId);
1317
+ return (ud ? getCompleteUserName(ud.domain, ud.name) : undefined) ?? String(rawValue);
1318
+ });
1319
+ return;
1320
+ }
1321
+ // Data e numero vengono mostrati con il formato definito sulle proprietà estese della colonna
1322
+ const { format, formatCulture } = getColumnFormatInfo(col);
1323
+ if (col.dataType === DataColumnTypes.DateTime) {
1324
+ getters.set(dataField, (rowData) => getDateDisplayText(rowData?.[dataField], format, formatCulture));
1325
+ return;
1326
+ }
1327
+ if (col.dataType === DataColumnTypes.Number) {
1328
+ getters.set(dataField, (rowData) => getNumberDisplayText(rowData?.[dataField], format, formatCulture));
1329
+ }
1330
+ });
1331
+ return getters;
1332
+ }, [searchResult, getDataListItem, getUserItem, getCompleteUserName]);
1333
+ const customizeColumns = useMemo(() => customizeSearchByDisplayText(searchTextGetters), [searchTextGetters]);
1143
1334
  /**
1144
1335
  * Effect che carica i dati necessari e genera le colonne della griglia.
1145
1336
  * Esegue il caricamento delle cache (DataList e UserID) in parallelo prima di generare le colonne,
@@ -1209,6 +1400,14 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1209
1400
  let newDataSource = searchResultDescriptorToSimpleArray(searchResult);
1210
1401
  setDataSource(newDataSource);
1211
1402
  }, [lastUpdateSearchTime]);
1403
+ const gridSorting = useMemo(() => ({ mode: 'multiple' }), []);
1404
+ const gridSelection = useMemo(() => ({ mode: allowMultipleSelection ? 'multiple' : 'single' }), [allowMultipleSelection]);
1405
+ const useNativeScrollbar = SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1;
1406
+ const gridScrolling = useMemo(() => ({
1407
+ columnRenderingMode: 'virtual',
1408
+ rowRenderingMode: 'standard',
1409
+ useNative: useNativeScrollbar
1410
+ }), [useNativeScrollbar]);
1212
1411
  // Handles selection change in the data grid
1213
1412
  const handleSelectionChange = useCallback((e) => {
1214
1413
  if (onSelectionChanged === undefined)
@@ -1231,6 +1430,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1231
1430
  // Era sull'icona, non fare nulla
1232
1431
  return;
1233
1432
  }
1433
+ // Due click rapidi su un'icona della colonna azioni non devono aprire anche il form
1434
+ if (e.event?.target.closest('.tm-row-actions'))
1435
+ return;
1234
1436
  // Verifica che il documento abbia TID e DID validi prima di procedere
1235
1437
  if (!e.data.TID || !e.data.DID) {
1236
1438
  ShowAlert({
@@ -1244,8 +1446,43 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1244
1446
  onFocusedItemChanged?.(e.data);
1245
1447
  onDblClick();
1246
1448
  }, [onDblClick]);
1449
+ /**
1450
+ * Colonna delle azioni di riga, in testa alla griglia (subito dopo la checkbox di selezione, che
1451
+ * DevExtreme tiene sempre per prima). Vive solo qui e non nello stato `columns`, che è quello
1452
+ * sincronizzato col padre per l'esportazione: una colonna senza dataField finirebbe in Excel come
1453
+ * colonna vuota.
1454
+ *
1455
+ * Le operazioni sono lette da una ref perché `operationItems` è ricostruito a ogni render del
1456
+ * padre: usarlo come dipendenza renderebbe nuova la colonna, e con essa `dataColumns`, ad ogni
1457
+ * giro. La cellRender le rilegge a ogni ridisegno della griglia, quindi resta comunque aggiornata.
1458
+ */
1459
+ const operationItemsRef = useRef(operationItems);
1460
+ operationItemsRef.current = operationItems;
1461
+ const actionsColumn = useMemo(() => ({
1462
+ caption: '',
1463
+ width: 110,
1464
+ alignment: 'center',
1465
+ allowSorting: false,
1466
+ allowFiltering: false,
1467
+ allowHeaderFiltering: false,
1468
+ allowResizing: false,
1469
+ allowReordering: false,
1470
+ allowHiding: false,
1471
+ allowExporting: false,
1472
+ showInColumnChooser: false,
1473
+ cssClass: 'tm-row-actions',
1474
+ cellRender: (cellData) => (_jsx("div", { style: rowActionsStyles.cell, children: rowActions.map(action => {
1475
+ const operation = findOperationItemById(operationItemsRef.current, action.operationId);
1476
+ if (!operation || operation.visible === false)
1477
+ return null;
1478
+ if (action.isVisible && !action.isVisible(cellData.data))
1479
+ return null;
1480
+ return (_jsx("span", { style: rowActionsStyles.icon, title: operation.name, onClick: () => onRowActionRequest?.(action.operationId, cellData.data), children: action.icon }, action.operationId));
1481
+ }) })),
1482
+ }), [onRowActionRequest]);
1247
1483
  const dataColumns = useMemo(() => {
1248
1484
  return [
1485
+ ...(showActionsColumn ? [actionsColumn] : []),
1249
1486
  {
1250
1487
  dataType: "object",
1251
1488
  dataField: 'FILEEXT',
@@ -1259,7 +1496,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1259
1496
  },
1260
1497
  ...columns.filter(col => col.dataField !== 'FILEEXT')
1261
1498
  ];
1262
- }, [columns, onDownloadDcmtsAsync, openInOffice]);
1499
+ }, [columns, onDownloadDcmtsAsync, openInOffice, showActionsColumn, actionsColumn]);
1263
1500
  const onContentReady = useCallback((e) => {
1264
1501
  if (e === undefined)
1265
1502
  return;
@@ -1270,21 +1507,22 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
1270
1507
  setVisibleItems(visibleData);
1271
1508
  }, []);
1272
1509
  useEffect(() => { onVisibleItemChanged?.(visibleItems); }, [visibleItems]);
1273
- 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: {
1510
+ const defaultCounterItems = dcmtsReturned !== undefined && dcmtsFound !== undefined
1511
+ ? new Map([
1512
+ [CounterItemKey.allItems, {
1513
+ key: CounterItemKey.allItems,
1274
1514
  show: true,
1275
- items: dcmtsReturned !== undefined && dcmtsFound !== undefined
1276
- ? new Map([
1277
- [CounterItemKey.allItems, {
1278
- key: CounterItemKey.allItems,
1279
- show: true,
1280
- caption: `${dcmtsReturned} restituiti su ${dcmtsFound} trovati`,
1281
- value: `${dcmtsReturned}/${dcmtsFound}`,
1282
- icon: _jsx(IconAll, {}),
1283
- order: -1
1284
- }]
1285
- ])
1286
- : undefined
1287
- } })] });
1515
+ caption: `${dcmtsReturned} restituiti su ${dcmtsFound} trovati`,
1516
+ value: `${dcmtsReturned}/${dcmtsFound}`,
1517
+ icon: _jsx(IconAll, {}),
1518
+ order: -1
1519
+ }]
1520
+ ])
1521
+ : undefined;
1522
+ const gridCounterConfig = showCounterConfig
1523
+ ? { ...counterConfig, show: true, items: counterConfig?.items ?? defaultCounterItems }
1524
+ : { show: false };
1525
+ 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, customizeColumns: customizeColumns, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: disableAutoFocus ? undefined : Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearchTMDatagrid, showFilterPanel: true, sorting: gridSorting, selection: gridSelection, scrolling: gridScrolling, pageSize: pageSize, compactPager: compactPager, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: operationItems, counterConfig: gridCounterConfig })] });
1288
1526
  };
1289
1527
  //#region TMSearchResultSelector
1290
1528
  const StyledItemTemplate = styled.div `
@@ -1345,14 +1583,29 @@ const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCa
1345
1583
  }
1346
1584
  }
1347
1585
  }, [sortedCategories.length, autoSelectFirst]);
1586
+ // La preselezione esterna va applicata una volta per valore: riapplicarla a ogni cambio di
1587
+ // searchResults faceva sì che un refresh annullasse la navigazione dell'utente.
1588
+ const appliedSelectedTIDRef = useRef(undefined);
1348
1589
  useEffect(() => {
1349
- if (selectedTID !== undefined) {
1350
- const found = searchResults.find(r => Number(r.fromTID) === selectedTID);
1351
- handleSelect(found);
1590
+ // Richiesta ritirata: dimentichiamo il valore applicato, così se il chiamante richiede di
1591
+ // nuovo lo stesso TID la preselezione riparte
1592
+ if (selectedTID === undefined) {
1593
+ appliedSelectedTIDRef.current = undefined;
1594
+ return;
1352
1595
  }
1596
+ if (appliedSelectedTIDRef.current === selectedTID)
1597
+ return;
1598
+ const found = searchResults.find(r => Number(r.fromTID) === selectedTID);
1599
+ if (!found)
1600
+ return; // risultati non ancora arrivati: riproviamo al prossimo giro
1601
+ appliedSelectedTIDRef.current = selectedTID;
1602
+ handleSelect(found);
1353
1603
  }, [selectedTID, searchResults]);
1354
- const handleSelect = useCallback((result) => {
1355
- if (selectedResult !== result && result !== undefined) {
1604
+ // force = true per i click dell'utente: la selezione va notificata anche se il tipo documento è già quello selezionato
1605
+ const handleSelect = useCallback((result, force = false) => {
1606
+ if (result === undefined)
1607
+ return;
1608
+ if (selectedResult !== result || force) {
1356
1609
  setSelectedResult(result);
1357
1610
  onSelectionChanged?.(result);
1358
1611
  }
@@ -1397,32 +1650,88 @@ const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCa
1397
1650
  renderItem: (result, isSelected) => (_jsxs("div", { style: { width: '100%', padding: '5px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx(TMTidViewer, { tid: result.fromTID, did: Number(result.dtdResult?.rows?.[0]?.[1]), showIcon: true }), _jsx("div", { style: { padding: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'white', color: 'gray', borderRadius: 3 }, children: result.dcmtsReturned })] })),
1398
1651
  itemHeight: 40
1399
1652
  })), [sortedCategories, groupedResults, searchResults]);
1400
- const renderCategoryItems = (category) => (_jsx("div", { style: { padding: '5px', height: '100%', overflow: 'auto' }, children: groupedResults[category].map((result, index) => (_jsx(MemoizedStyledItemTemplate, { "$isSelected": selectedResult === result, onClick: () => handleSelect(result), children: renderItemTemplate(result) }, index))) }));
1653
+ const renderCategoryItems = (category) => (_jsx("div", { style: { padding: '5px', height: '100%', overflow: 'auto' }, children: groupedResults[category].map((result, index) => (_jsx(MemoizedStyledItemTemplate, { $isSelected: selectedResult === result, onClick: () => handleSelect(result, true), children: renderItemTemplate(result) }, index))) }));
1401
1654
  if (disableAccordionIfSingleCategory && sortedCategories.length === 1) {
1402
1655
  const category = sortedCategories[0];
1403
1656
  return renderCategoryItems(category);
1404
1657
  }
1405
- return (_jsx(TMAccordionNew, { groups: accordionGroups, selectedItem: selectedResult, onSelectedItemChange: (result) => handleSelect(result) }));
1658
+ return (_jsx(TMAccordionNew, { groups: accordionGroups, selectedItem: selectedResult, onSelectedItemChange: (result) => handleSelect(result, true) }));
1406
1659
  };
1407
1660
  //#endregion TMSearchResultSelector
1408
- const PanelDisabledStateHandler = ({ isBoardDisabled }) => {
1409
- const { setPanelVisibilityById, setToolbarButtonDisabled } = useTMPanelManagerContext();
1661
+ /** Senza UI: vive dentro il provider e passa a onBack, via ref, l'azione "mostra il pannello lista". */
1662
+ const ShowMainPanelBridge = ({ showMainPanelRef }) => {
1663
+ const { setPanelVisibilityById } = useTMPanelManagerContext();
1410
1664
  useEffect(() => {
1411
- if (isBoardDisabled) {
1412
- setToolbarButtonDisabled('tmBlog', true);
1413
- setPanelVisibilityById('tmBlog', false);
1414
- }
1415
- else {
1416
- setToolbarButtonDisabled('tmBlog', false);
1417
- }
1665
+ // Su mobile mostrare un pannello nasconde gli altri; su desktop la lista è già visibile
1666
+ showMainPanelRef.current = () => setPanelVisibilityById('tmSearchResult', true);
1667
+ return () => { showMainPanelRef.current = null; };
1668
+ }, [setPanelVisibilityById, showMainPanelRef]);
1669
+ return null;
1670
+ };
1671
+ /** Senza UI: allinea i pannelli quando il layout viene salvato da un altro risultato di ricerca già aperto */
1672
+ const SavedLayoutSyncHandler = ({ disabledPanelIds }) => {
1673
+ const { panelVisibility, panelDimensions, maximizedPanels, isResizingActive, setPanelVisibilityById, setPanelDimensionsById } = useTMPanelManagerContext();
1674
+ // Il layout è condiviso fra i risultati aperti: si sottoscrive la firma, che è una stringa, perché
1675
+ // React confronta le letture per riferimento e su un oggetto ri-renderizzerebbe all'infinito
1676
+ const savedLayoutSignature = useSyncExternalStore(subscribeSearchResultPanelLayout, getSearchResultPanelLayoutSignature);
1677
+ useEffect(() => {
1678
+ // Con un pannello massimizzato o un separatore in movimento comanda l'utente di questa istanza
1679
+ if (maximizedPanels.length > 0 || isResizingActive)
1680
+ return;
1681
+ // Al mount panelVisibility è ancora vuota: lo stato iniziale lo applica il provider.
1682
+ // Un pannello disabilitato qui non va aperto, gli altri si allineano comunque
1683
+ const panelsToApply = Object.entries(getSearchResultPanelLayout())
1684
+ .filter(([id, state]) => panelVisibility[id] !== undefined && !(state?.visible && disabledPanelIds.includes(id)));
1685
+ panelsToApply.forEach(([id, state]) => {
1686
+ const isVisible = state?.visible ?? false;
1687
+ if (panelVisibility[id] !== isVisible)
1688
+ setPanelVisibilityById(id, isVisible);
1689
+ });
1690
+ // Le larghezze salvate valgono come pesi, non come percentuali: qui si normalizzano sui soli pannelli
1691
+ // che questo risultato apre davvero, così lo spazio di quelli scartati non resta vuoto
1692
+ const panelsToShow = panelsToApply.filter(([, state]) => state?.visible && parseFloat(state.width) > 0 && parseFloat(state.height) > 0);
1693
+ const weightTotal = panelsToShow.reduce((total, [, state]) => total + parseFloat(state.width), 0);
1694
+ if (weightTotal <= 0)
1695
+ return;
1696
+ // Dopo la visibilità, che accoda un suo ricalcolo delle quote: così restano quelle salvate.
1697
+ // Le quote sono arrotondate, altrimenti due risultati si rincorrono sugli ultimi decimali
1698
+ panelsToShow.forEach(([id, state]) => {
1699
+ const width = `${Math.round(parseFloat(state.width) / weightTotal * 10000) / 100}%`;
1700
+ if (panelDimensions[id]?.width === width && panelDimensions[id]?.height === state.height)
1701
+ return;
1702
+ setPanelDimensionsById(id, width, state.height);
1703
+ });
1704
+ }, [savedLayoutSignature]);
1705
+ return null;
1706
+ };
1707
+ const PanelDisabledStateHandler = ({ isBoardDisabled, savedBlogVisible = false }) => {
1708
+ const { panelVisibility, setPanelVisibilityById, setToolbarButtonDisabled } = useTMPanelManagerContext();
1709
+ const isBlogVisible = panelVisibility['tmBlog'] ?? false;
1710
+ useEffect(() => {
1711
+ setToolbarButtonDisabled('tmBlog', isBoardDisabled);
1418
1712
  }, [isBoardDisabled]);
1713
+ /**
1714
+ * Senza bacheca nel tipo documento il pannello va chiuso ogni volta che ricompare, non solo al cambio
1715
+ * di stato: il layout salvato viene applicato dopo questo effetto e può riportarlo a video.
1716
+ * È un effetto di layout per chiuderlo prima che venga disegnato, altrimenti lampeggia.
1717
+ */
1718
+ useLayoutEffect(() => {
1719
+ if (isBoardDisabled && isBlogVisible)
1720
+ setPanelVisibilityById('tmBlog', false);
1721
+ }, [isBoardDisabled, isBlogVisible]);
1722
+ // Bacheca di nuovo prevista: si riapre come da layout salvato. isBlogVisible non è fra le dipendenze,
1723
+ // altrimenti riaprirebbe il pannello appena chiuso dall'utente
1724
+ useEffect(() => {
1725
+ if (!isBoardDisabled && savedBlogVisible && !isBlogVisible)
1726
+ setPanelVisibilityById('tmBlog', true);
1727
+ }, [isBoardDisabled, savedBlogVisible]);
1419
1728
  return null;
1420
1729
  };
1421
- const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, onBack }) => {
1730
+ const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, isPageVisible = true, onBack }) => {
1422
1731
  const { setPanelVisibilityById, toggleMaximize, isResizingActive, countVisibleLeafPanels } = useTMPanelManagerContext();
1423
1732
  const deviceType = useDeviceType();
1424
1733
  const isMobile = deviceType === DeviceType.MOBILE;
1425
- return (_jsx(TMDcmtPreview, { dcmtData: currentDcmt, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, onBack: onBack, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, isResizingActive: isResizingActive, isVisible: isVisible }, refreshPreviewTrigger));
1734
+ return (_jsx(TMDcmtPreview, { dcmtData: currentDcmt, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, onBack: onBack, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, isResizingActive: isResizingActive, isVisible: isVisible && isPageVisible }, refreshPreviewTrigger));
1426
1735
  };
1427
1736
  // Styled Components
1428
1737
  const StyledPlaceholder = styled.div `