@topconsultnpm/sdkui-react 6.22.0-dev3.3 → 6.22.0-dev3.31

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 (173) hide show
  1. package/lib/components/NewComponents/ContextMenu/styles.d.ts +45 -43
  2. package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +67 -60
  3. package/lib/components/base/Styled.d.ts +93 -77
  4. package/lib/components/base/Styled.js +7 -5
  5. package/lib/components/base/TMAlert.js +6 -8
  6. package/lib/components/base/TMAreaManager.js +53 -55
  7. package/lib/components/base/TMCounterBar.d.ts +2 -2
  8. package/lib/components/base/TMCounterContainer.d.ts +2 -1
  9. package/lib/components/base/TMCustomButton.d.ts +1 -1
  10. package/lib/components/base/TMCustomButton.js +40 -48
  11. package/lib/components/base/TMDataGrid.js +2 -2
  12. package/lib/components/base/TMDataGridExportForm.d.ts +2 -1
  13. package/lib/components/base/TMFileManager.d.ts +2 -1
  14. package/lib/components/base/TMFileManagerDataGridView.d.ts +2 -1
  15. package/lib/components/base/TMFileManagerThumbnailItems.d.ts +1 -1
  16. package/lib/components/base/TMFileManagerThumbnailsView.d.ts +1 -1
  17. package/lib/components/base/TMFileManagerUtils.d.ts +4 -6
  18. package/lib/components/base/TMLayout.d.ts +4 -4
  19. package/lib/components/base/TMList.d.ts +2 -1
  20. package/lib/components/base/TMListView.d.ts +1 -1
  21. package/lib/components/base/TMPanel.js +2 -2
  22. package/lib/components/base/TMSpinner.d.ts +4 -2
  23. package/lib/components/base/TMSpinner.js +9 -7
  24. package/lib/components/base/TMTab.d.ts +4 -3
  25. package/lib/components/base/TMTabControl.d.ts +106 -0
  26. package/lib/components/base/TMTabControl.js +664 -0
  27. package/lib/components/base/TMTooltip.d.ts +1 -1
  28. package/lib/components/base/TMTreeView.d.ts +14 -8
  29. package/lib/components/base/TMTreeView.js +33 -1
  30. package/lib/components/base/TMUserAvatar.d.ts +2 -1
  31. package/lib/components/base/TMVilViewer.d.ts +2 -1
  32. package/lib/components/base/TMWaitPanel.d.ts +3 -2
  33. package/lib/components/base/TMWaitPanel.js +1 -1
  34. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  35. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  36. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  37. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  38. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
  39. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  40. package/lib/components/base/waitPanel/index.d.ts +1 -0
  41. package/lib/components/base/waitPanel/index.js +1 -0
  42. package/lib/components/choosers/TMDynDataListItemChooser.d.ts +1 -1
  43. package/lib/components/choosers/TMInvoiceRetrieveFormats.d.ts +2 -1
  44. package/lib/components/choosers/TMOrderRetrieveFormats.d.ts +2 -1
  45. package/lib/components/choosers/TMPathChooser.js +17 -34
  46. package/lib/components/choosers/TMQuickSearchInfo.d.ts +2 -1
  47. package/lib/components/choosers/TMRelationChooser.d.ts +3 -3
  48. package/lib/components/choosers/TMUserChooser.d.ts +2 -2
  49. package/lib/components/editors/TMEditorStyled.d.ts +20 -42
  50. package/lib/components/editors/TMFormulaEditor.d.ts +1 -1
  51. package/lib/components/editors/TMHtmlContentDisplay.d.ts +2 -1
  52. package/lib/components/editors/TMHtmlEditor.d.ts +2 -1
  53. package/lib/components/editors/TMMetadataValues.js +5 -5
  54. package/lib/components/editors/TMTreeDropDown.d.ts +1 -1
  55. package/lib/components/features/archive/TMArchive.js +7 -2
  56. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +28 -0
  57. package/lib/components/features/assistant/TMHelpOnLineChat.js +888 -0
  58. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.d.ts +1 -1
  59. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
  60. package/lib/components/features/blog/TMBlogCommentForm.d.ts +2 -1
  61. package/lib/components/features/documents/TMBatchUpdateForm.js +3 -4
  62. package/lib/components/features/documents/TMCopyToFolderForm.js +50 -42
  63. package/lib/components/features/documents/TMDcmtForm.js +51 -62
  64. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +1 -1
  65. package/lib/components/features/documents/TMDcmtIcon.d.ts +2 -3
  66. package/lib/components/features/documents/TMDcmtIcon.js +3 -5
  67. package/lib/components/features/documents/TMDcmtPreview.d.ts +8 -6
  68. package/lib/components/features/documents/TMDcmtPreview.js +5 -3
  69. package/lib/components/features/documents/TMDcmtTasks.d.ts +2 -1
  70. package/lib/components/features/documents/TMDragDropOverlay.d.ts +1 -1
  71. package/lib/components/features/documents/TMFileUploader.js +26 -26
  72. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  73. package/lib/components/features/documents/TMMasterDetailDcmts.js +54 -129
  74. package/lib/components/features/documents/TMMasterInfoFields.d.ts +2 -1
  75. package/lib/components/features/documents/TMMasterInfoFields.js +3 -2
  76. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +2 -1
  77. package/lib/components/features/documents/TMMergeToPdfForm.js +56 -54
  78. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +2 -1
  79. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +2 -5
  80. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +5 -5
  81. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +3 -3
  82. package/lib/components/features/documents/TMRelationViewer.d.ts +16 -9
  83. package/lib/components/features/documents/TMRelationViewer.js +238 -166
  84. package/lib/components/features/documents/relationTreeState.d.ts +32 -0
  85. package/lib/components/features/documents/relationTreeState.js +89 -0
  86. package/lib/components/features/search/TMDcmtCheckoutInfoForm.d.ts +2 -1
  87. package/lib/components/features/search/TMMetadataOutputForm.d.ts +2 -1
  88. package/lib/components/features/search/TMMetadataSorterForm.d.ts +2 -1
  89. package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
  90. package/lib/components/features/search/TMSearch.d.ts +1 -1
  91. package/lib/components/features/search/TMSearch.js +68 -9
  92. package/lib/components/features/search/TMSearchQueryPanel.d.ts +9 -3
  93. package/lib/components/features/search/TMSearchResult.d.ts +5 -0
  94. package/lib/components/features/search/TMSearchResult.js +19 -12
  95. package/lib/components/features/search/TMSearchResultFloatingActionButton.d.ts +1 -1
  96. package/lib/components/features/search/TMSignatureInfoContent.d.ts +2 -1
  97. package/lib/components/features/search/TMTreeSelector.js +38 -36
  98. package/lib/components/features/search/TMViewHistoryDcmt.d.ts +2 -1
  99. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  100. package/lib/components/features/tasks/TMTaskForm.d.ts +1 -1
  101. package/lib/components/features/tasks/TMTaskFormUtils.d.ts +10 -10
  102. package/lib/components/features/tasks/TMTasksAgenda.d.ts +1 -1
  103. package/lib/components/features/tasks/TMTasksCalendar.d.ts +1 -1
  104. package/lib/components/features/tasks/TMTasksHeader.d.ts +1 -1
  105. package/lib/components/features/tasks/TMTasksPanelContent.d.ts +1 -1
  106. package/lib/components/features/tasks/TMTasksUtilsView.d.ts +5 -5
  107. package/lib/components/features/tasks/TMTasksView.d.ts +1 -1
  108. package/lib/components/features/tasks/TMTasksView.js +96 -101
  109. package/lib/components/features/wg/TMWGsCopyMoveForm.d.ts +3 -2
  110. package/lib/components/features/wg/TMWGsCopyMoveForm.js +91 -91
  111. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +8 -7
  112. package/lib/components/features/workflow/diagram/WorkitemRecipientsEditor.d.ts +3 -1
  113. package/lib/components/forms/Login/ChangePasswordInputs.d.ts +3 -1
  114. package/lib/components/forms/Login/Chooser.d.ts +2 -2
  115. package/lib/components/forms/TMChooserForm.d.ts +2 -1
  116. package/lib/components/forms/TMResultDialog.js +1 -1
  117. package/lib/components/forms/TMSaveForm.d.ts +4 -4
  118. package/lib/components/grids/TMBlogAttachments.d.ts +2 -1
  119. package/lib/components/grids/TMBlogHeader.d.ts +1 -1
  120. package/lib/components/grids/TMBlogsPost.d.ts +1 -1
  121. package/lib/components/grids/TMBlogsPost.js +92 -96
  122. package/lib/components/grids/TMBlogsPostUtils.d.ts +9 -9
  123. package/lib/components/grids/TMValidationItemsList.d.ts +2 -1
  124. package/lib/components/index.d.ts +6 -0
  125. package/lib/components/index.js +5 -0
  126. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +2 -1
  127. package/lib/components/layout/panelManager/TMPanelManagerContext.d.ts +2 -2
  128. package/lib/components/layout/panelManager/TMPanelManagerToolbar.d.ts +5 -6
  129. package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.d.ts +2 -2
  130. package/lib/components/layout/panelManager/TMPanelWrapper.d.ts +2 -2
  131. package/lib/components/pages/TMPage.d.ts +1 -1
  132. package/lib/components/pages/TMPage.js +29 -31
  133. package/lib/components/query/TMQueryEditor.d.ts +17 -10
  134. package/lib/components/query/TMQueryEditor.js +2 -1
  135. package/lib/components/settings/SettingsAppearance.d.ts +2 -1
  136. package/lib/components/sidebar/TMAboutApp.d.ts +2 -1
  137. package/lib/components/sidebar/TMCommandsPanel.d.ts +4 -4
  138. package/lib/components/sidebar/TMHeader.d.ts +4 -3
  139. package/lib/components/sidebar/TMHeader.js +2 -2
  140. package/lib/components/viewers/TMDataListItemViewer.d.ts +3 -2
  141. package/lib/components/viewers/TMDataUserIdItemViewer.d.ts +3 -2
  142. package/lib/components/viewers/TMMidViewer.d.ts +2 -2
  143. package/lib/components/viewers/TMTidViewer.d.ts +2 -2
  144. package/lib/helper/Enum_Localizator.js +1 -0
  145. package/lib/helper/SDKUI_Localizator.d.ts +24 -0
  146. package/lib/helper/SDKUI_Localizator.js +240 -0
  147. package/lib/helper/TMAppUpdatedModal.d.ts +2 -1
  148. package/lib/helper/TMAppUpdatedModal.js +93 -93
  149. package/lib/helper/TMCommandsContextMenu.d.ts +1 -1
  150. package/lib/helper/TMIcons.d.ts +280 -280
  151. package/lib/helper/TMPdfViewer.d.ts +2 -1
  152. package/lib/helper/TMToppyMessage.d.ts +2 -2
  153. package/lib/helper/TMUtils.d.ts +10 -4
  154. package/lib/helper/ZipManager.d.ts +1 -1
  155. package/lib/helper/ZipManager.js +2 -1
  156. package/lib/helper/dcmtsHelper.d.ts +2 -0
  157. package/lib/helper/dcmtsHelper.js +3 -1
  158. package/lib/helper/helpers.d.ts +1 -1
  159. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  160. package/lib/hooks/useCheckInOutOperations.js +14 -34
  161. package/lib/hooks/useDcmtOperations.d.ts +3 -12
  162. package/lib/hooks/useDcmtOperations.js +187 -179
  163. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  164. package/lib/hooks/useDocumentOperations.js +14 -25
  165. package/lib/hooks/useInputDialog.d.ts +2 -1
  166. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
  167. package/lib/hooks/useMultiMasterDetailDcmts.js +65 -67
  168. package/lib/hooks/usePreventFileDrop.js +9 -0
  169. package/lib/hooks/useWorkflowApprove.js +33 -17
  170. package/lib/services/caseflow/CaseFlowService.d.ts +36 -6
  171. package/lib/services/caseflow/CaseFlowService.js +55 -14
  172. package/lib/services/platform_services.d.ts +3 -2
  173. package/package.json +71 -71
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import { FileDescriptor, FileFormats } from '@topconsultnpm/sdk-ts';
2
3
  export declare const useInputCvtFormatDialog: () => [() => Promise<FileFormats>, () => JSX.Element];
3
4
  export declare const useFileDialog: () => {
@@ -5,7 +6,7 @@ export declare const useFileDialog: () => {
5
6
  };
6
7
  export declare const useInputAttachmentsDialog: () => {
7
8
  openConfirmAttachmentsDialog: (list: FileDescriptor[]) => Promise<string[] | undefined>;
8
- ConfirmAttachmentsDialog: () => import("react/jsx-runtime").JSX.Element | null;
9
+ ConfirmAttachmentsDialog: () => React.JSX.Element | null;
9
10
  };
10
11
  export type FileSourceType = 'filesystem' | 'scanner-new' | 'scanner-edit' | undefined;
11
12
  export declare const useFileSourceDialog: () => [(hasExistingFile?: boolean) => Promise<FileSourceType>, () => JSX.Element];
@@ -16,19 +16,6 @@ export type MidValue = {
16
16
  mid: number;
17
17
  value: string;
18
18
  };
19
- /** Stato del wait panel: le chiavi sono quelle dei props di TMLayoutWaitingContainer */
20
- export type WaitPanelState = {
21
- showWaitPanel: boolean;
22
- waitPanelTitle: string;
23
- showWaitPanelPrimary: boolean;
24
- waitPanelTextPrimary: string;
25
- waitPanelValuePrimary: number;
26
- waitPanelMaxValuePrimary: number;
27
- showWaitPanelSecondary: boolean;
28
- waitPanelTextSecondary: string;
29
- waitPanelValueSecondary: number;
30
- waitPanelMaxValueSecondary: number;
31
- };
32
19
  interface UseMultiMasterDetailDcmtsParams {
33
20
  /** Tipo documento del master */
34
21
  dtd: DcmtTypeDescriptor;
@@ -108,6 +95,4 @@ export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose, can
108
95
  setStopOnFirstError: import("react").Dispatch<import("react").SetStateAction<boolean>>;
109
96
  isArchiving: boolean;
110
97
  archiveAllAsync: () => Promise<void>;
111
- waitPanel: WaitPanelState;
112
- abortController: AbortController | undefined;
113
98
  };
@@ -2,9 +2,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtOpers, GeneralRetrieveFormats, MetadataDataDomains, MetadataDataTypes, RelationCacheService, RelationTypes, ResultTypes, RetrieveFileOptions, SDK_Globals, SystemMIDsAsNumber } from '@topconsultnpm/sdk-ts';
3
3
  import { MetadataValueDescriptorEx } from '../ts';
4
4
  import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, handleArchiveVisibility, hasMultiDetailLayout, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
5
- import { getDcmtMetadataByMid, getUserMetadataSorted, mapAssociationsFromMetadata } from '../helper/dcmtsHelper';
5
+ import { canViewMetadata, getDcmtMetadataByMid, getUserMetadataSorted, mapAssociationsFromMetadata } from '../helper/dcmtsHelper';
6
6
  import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../components/base/TMPopUp';
7
7
  import TMSpinner from '../components/base/TMSpinner';
8
+ import { TMWaitPanelManager } from '../components/base/waitPanel';
8
9
  import ShowAlert from '../components/base/TMAlert';
9
10
  import { TMResultManager } from '../components/forms/TMResultDialog';
10
11
  import { useDataListItem } from './useDataListItem';
@@ -17,18 +18,6 @@ export var MultiDetailArchivePhase;
17
18
  /** Riepilogo delle archiviazioni */
18
19
  MultiDetailArchivePhase[MultiDetailArchivePhase["ArchiveSummary"] = 1] = "ArchiveSummary";
19
20
  })(MultiDetailArchivePhase || (MultiDetailArchivePhase = {}));
20
- const CLOSED_WAIT_PANEL = {
21
- showWaitPanel: false,
22
- waitPanelTitle: '',
23
- showWaitPanelPrimary: false,
24
- waitPanelTextPrimary: '',
25
- waitPanelValuePrimary: 0,
26
- waitPanelMaxValuePrimary: 0,
27
- showWaitPanelSecondary: false,
28
- waitPanelTextSecondary: '',
29
- waitPanelValueSecondary: 0,
30
- waitPanelMaxValueSecondary: 0,
31
- };
32
21
  /**
33
22
  * Logica dell'archiviazione multipla dei dettagli: relazioni, sezioni, righe del riepilogo e
34
23
  * archiviazione. Il rendering sta in TMMultiMasterDetailDcmtsForm
@@ -76,12 +65,11 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
76
65
  // Archiviazione finale
77
66
  const [stopOnFirstError, setStopOnFirstError] = useState(true);
78
67
  const [isArchiving, setIsArchiving] = useState(false);
79
- const [waitPanel, setWaitPanel] = useState(CLOSED_WAIT_PANEL);
80
- const abortControllerRef = useRef(undefined);
81
68
  const selectedItem = detailRelationItems.find(item => item.relation === selectedRelation);
82
69
  const hasMultipleTypes = detailRelationItems.length > 1;
83
70
  const canUpdateMaster = canUpdateMasterCallback(dtd, masterDcmt);
84
- const masterFieldsCount = useMemo(() => getUserMetadataSorted(dtd).length, [dtd]);
71
+ // Stessi metadati delle card del master (vedi TMMasterInfoFields): quelli non visibili non contano
72
+ const masterFieldsCount = useMemo(() => getUserMetadataSorted(dtd).filter(canViewMetadata).length, [dtd]);
85
73
  // Nel riepilogo si mostrano solo le righe del tipo selezionato
86
74
  const currentRows = useMemo(() => archiveRows.filter(r => r.__tid === selectedRelation?.detailTID), [archiveRows, selectedRelation?.detailTID]);
87
75
  const archivedDetailsCount = archivedDetailResults.reduce((total, r) => total + (r.dcmtsFound ?? r.jsonResult?.length ?? 0), 0);
@@ -619,44 +607,68 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
619
607
  dataTypeByMid.set(md.id, md.dataType); });
620
608
  // Collegamenti della relazione valorizzati dal master (prevalgono sui valori del form)
621
609
  const associationMids = await getMasterAssociationsForDetailAsync(selectedRelation);
622
- // Barra primaria: avanzamento documenti. Barra secondaria: upload del file corrente
623
- const abortController = new AbortController();
624
- abortControllerRef.current = abortController;
625
- setWaitPanel({
626
- ...CLOSED_WAIT_PANEL,
627
- showWaitPanel: true,
628
- waitPanelTitle: SDKUI_Localizator.Archiving,
629
- showWaitPanelPrimary: rowsToArchive.length > 1,
630
- waitPanelMaxValuePrimary: rowsToArchive.length,
631
- showWaitPanelSecondary: true,
610
+ // Con piu' righe: primaria sui documenti, secondaria sui byte. Con una riga sola solo i byte.
611
+ const isBatch = rowsToArchive.length > 1;
612
+ const waitPanel = TMWaitPanelManager.show({
613
+ title: SDKUI_Localizator.Archiving,
614
+ max: isBatch ? rowsToArchive.length : 0,
615
+ cancelable: true,
616
+ showSecondary: isBatch,
617
+ showCounters: isBatch,
618
+ primaryLabel: isBatch ? SDKUI_Localizator.Documents : undefined,
619
+ secondaryLabel: isBatch ? 'File corrente' : undefined,
632
620
  });
621
+ const publishBytes = (loaded, total) => {
622
+ const text = total > 0
623
+ ? `Uploading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}`
624
+ : '';
625
+ if (isBatch)
626
+ waitPanel.setSecondary({ value: loaded, max: total, text });
627
+ else
628
+ waitPanel.update({ value: loaded, max: total, subtitle: text });
629
+ };
633
630
  const result = [];
634
631
  const archivedRowIds = new Set();
635
- for (let i = 0; i < rowsToArchive.length; i++) {
636
- if (abortController.signal.aborted) {
637
- result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
638
- break;
639
- }
640
- const rowData = rowsToArchive[i];
641
- try {
642
- setWaitPanel(prev => ({ ...prev, waitPanelTextPrimary: `${SDKUI_Localizator.Archiving} ${i + 1} / ${rowsToArchive.length}` }));
643
- const newDID = await archiveRowAsync(rowData, detailTID, dataTypeByMid, associationMids, abortController.signal);
644
- setWaitPanel(prev => ({ ...prev, waitPanelValuePrimary: i + 1 }));
645
- archivedRowIds.add(rowData.id);
646
- result.push({ rowIndex: i, id1: detailTID, id2: newDID, resultType: ResultTypes.SUCCESS });
647
- }
648
- catch (error) {
649
- const err = error;
650
- // Annullamento richiesto dall'utente: interrompe senza mostrare l'errore
651
- if (err?.name === 'CanceledError' || abortController.signal.aborted) {
632
+ try {
633
+ for (let i = 0; i < rowsToArchive.length; i++) {
634
+ // Con la conferma di interruzione aperta il loop resta in pausa qui
635
+ await waitPanel.waitWhilePaused();
636
+ if (waitPanel.aborted) {
652
637
  result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
638
+ waitPanel.warning();
653
639
  break;
654
640
  }
655
- result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.ERROR, description: getExceptionMessage(error) });
656
- if (stopOnFirstError)
657
- break;
641
+ if (isBatch)
642
+ waitPanel.setSubtitle(`${SDKUI_Localizator.Archiving} ${i + 1} / ${rowsToArchive.length}`);
643
+ const rowData = rowsToArchive[i];
644
+ try {
645
+ const newDID = await archiveRowAsync(rowData, detailTID, dataTypeByMid, associationMids, waitPanel.signal, publishBytes);
646
+ archivedRowIds.add(rowData.id);
647
+ result.push({ rowIndex: i, id1: detailTID, id2: newDID, resultType: ResultTypes.SUCCESS });
648
+ waitPanel.ok();
649
+ }
650
+ catch (error) {
651
+ const err = error;
652
+ // Annullamento richiesto dall'utente: interrompe senza mostrare l'errore
653
+ if (err?.name === 'CanceledError' || waitPanel.aborted) {
654
+ result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
655
+ waitPanel.warning();
656
+ break;
657
+ }
658
+ result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.ERROR, description: getExceptionMessage(error) });
659
+ waitPanel.error();
660
+ if (stopOnFirstError)
661
+ break;
662
+ }
663
+ // Avanza a elaborazione conclusa: la barra non arriva al 100% prima dell'ultimo documento
664
+ if (isBatch)
665
+ await waitPanel.step();
658
666
  }
659
667
  }
668
+ finally {
669
+ // Chiuso prima del riepilogo dei risultati
670
+ waitPanel.close();
671
+ }
660
672
  // Le righe archiviate escono dal riepilogo
661
673
  if (archivedRowIds.size > 0) {
662
674
  setArchiveRows(prev => prev.filter(r => !archivedRowIds.has(r.id)));
@@ -676,13 +688,11 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
676
688
  TMExceptionBoxManager.show({ exception: error });
677
689
  }
678
690
  finally {
679
- abortControllerRef.current = undefined;
680
- setWaitPanel(CLOSED_WAIT_PANEL);
681
691
  setIsArchiving(false);
682
692
  }
683
693
  };
684
- // Archiviazione di una singola riga, con avanzamento dell'upload sulla barra secondaria
685
- const archiveRowAsync = async (row, detailTID, dataTypeByMid, associationMids, signal) => {
694
+ // Archiviazione di una singola riga, con avanzamento dell'upload notificato a onBytes
695
+ const archiveRowAsync = async (row, detailTID, dataTypeByMid, associationMids, signal, onBytes) => {
686
696
  const ae = new ArchiveEngineByID(SDK_Globals.tmSession);
687
697
  ae.TID = detailTID;
688
698
  ae.Metadata_ClearAll();
@@ -714,23 +724,12 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
714
724
  });
715
725
  if (row.__file)
716
726
  ae.ArchivingFile = row.__file;
717
- let firstBlock = true;
718
- let maxFileSize = 0;
719
727
  return ae.ArchiveAsync(signal, (pd) => {
720
- if (firstBlock) {
721
- maxFileSize = pd.ProgressBarMaximum ?? 0;
722
- setWaitPanel(prev => ({ ...prev, waitPanelMaxValueSecondary: maxFileSize }));
723
- firstBlock = false;
724
- }
725
- setWaitPanel(prev => ({
726
- ...prev,
727
- waitPanelValueSecondary: pd.ProgressBarValue,
728
- waitPanelTextSecondary: `Uploading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`,
729
- }));
730
- if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
731
- setWaitPanel(prev => ({ ...prev, waitPanelValueSecondary: 0, waitPanelMaxValueSecondary: 0, waitPanelTextSecondary: '' }));
732
- firstBlock = true;
733
- }
728
+ // Upload completato: la barra si azzera per il file successivo
729
+ if (pd.ProgressBarValue === pd.ProgressBarMaximum)
730
+ onBytes(0, 0);
731
+ else
732
+ onBytes(pd.ProgressBarValue ?? 0, pd.ProgressBarMaximum ?? 0);
734
733
  });
735
734
  };
736
735
  return {
@@ -755,6 +754,5 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
755
754
  openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, copyArchivedDetailToRows,
756
755
  // Archiviazione
757
756
  stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync,
758
- waitPanel, abortController: abortControllerRef.current,
759
757
  };
760
758
  };
@@ -2,7 +2,12 @@ import { useEffect } from "react";
2
2
  const usePreventFileDrop = (allowedDropZones) => {
3
3
  useEffect(() => {
4
4
  const isOverAllowedZone = (event) => allowedDropZones.some((ref) => ref.current?.contains(event.target));
5
+ // Il blocco vale solo per i file trascinati da fuori: i drag interni all'applicazione
6
+ // (riordino di metadati, colonne, tab, ...) devono restare liberi di completarsi
7
+ const isFileDrag = (event) => event.dataTransfer?.types?.includes("Files") === true;
5
8
  const handleDragOver = (event) => {
9
+ if (!isFileDrag(event))
10
+ return;
6
11
  if (isOverAllowedZone(event)) {
7
12
  // Riafferma "copy" ad ogni dragover: altrimenti un "none"
8
13
  // impostato mentre si era fuori zona resta e mostra "not-allowed"
@@ -17,6 +22,8 @@ const usePreventFileDrop = (allowedDropZones) => {
17
22
  }
18
23
  };
19
24
  const handleDragEnter = (event) => {
25
+ if (!isFileDrag(event))
26
+ return;
20
27
  if (isOverAllowedZone(event)) {
21
28
  document.body.style.cursor = "default";
22
29
  }
@@ -28,6 +35,8 @@ const usePreventFileDrop = (allowedDropZones) => {
28
35
  document.body.style.cursor = "default";
29
36
  };
30
37
  const handleDrop = (event) => {
38
+ if (!isFileDrag(event))
39
+ return;
31
40
  if (!isOverAllowedZone(event)) {
32
41
  event.preventDefault();
33
42
  document.body.style.cursor = "default";
@@ -1,5 +1,25 @@
1
1
  import { useState, useEffect, useCallback } from 'react';
2
2
  import { SDK_Globals } from '@topconsultnpm/sdk-ts';
3
+ const WORKFLOW_APPROVE_CHANGED_EVENT = 'onWorkflowApproveChange';
4
+ /** Totale dei documenti da approvare: somma dei dcmtsFound delle singole categorie. */
5
+ const calcTotalDcmtsFound = (data) => {
6
+ return data.reduce((sum, item) => sum + (item.dcmtsFound || 0), 0);
7
+ };
8
+ /**
9
+ * Fetch in corso.
10
+ *
11
+ * La WFAppr non porta un contatore ma l'intera lista di approvazione con le righe
12
+ * dei documenti, ed è chiesta da più istanze del hook a ridosso l'una dell'altra
13
+ * (coda del login, pagina di approvazione, widget della home). Chi arriva mentre
14
+ * è già in volo aspetta la stessa risposta invece di aprire una seconda richiesta.
15
+ */
16
+ let inFlight;
17
+ /** Ricarica i dati e notifica tutte le istanze del hook. */
18
+ const load = async () => {
19
+ const result = await SDK_Globals.tmSession?.NewWorkflowEngine().WFApprAsync(1);
20
+ SDK_Globals.currentWorkflowApproveData = result ?? [];
21
+ window.dispatchEvent(new CustomEvent(WORKFLOW_APPROVE_CHANGED_EVENT));
22
+ };
3
23
  /**
4
24
  * Hook per gestire i dati di approvazione del workflow.
5
25
  * Fornisce il dataSource, lo stato di caricamento e una funzione per aggiornare i dati.
@@ -7,13 +27,10 @@ import { SDK_Globals } from '@topconsultnpm/sdk-ts';
7
27
  export const useWorkflowApprove = () => {
8
28
  const [workflowApproveData, setWorkflowApproveData] = useState(SDK_Globals.currentWorkflowApproveData);
9
29
  const [isLoading, setIsLoading] = useState(false);
10
- const [totalDcmtsFound, setTotalDcmtsFound] = useState(0);
11
- /**
12
- * Calcola il numero totale di dcmtsFound dai dati del workflow.
13
- */
14
- const calculateTotalDcmtsFound = useCallback((data) => {
15
- return data.reduce((sum, item) => sum + (item.dcmtsFound || 0), 0);
16
- }, []);
30
+ // Seminato dai dati già in cache: un'istanza montata a login concluso parte dal
31
+ // totale reale e non da zero, altrimenti chi confronta questo valore con il
32
+ // contatore della home vede una differenza inesistente e ricarica a vuoto.
33
+ const [totalDcmtsFound, setTotalDcmtsFound] = useState(() => calcTotalDcmtsFound(SDK_Globals.currentWorkflowApproveData));
17
34
  /**
18
35
  * Ottiene tutti i work items che corrispondono a un determinato DID.
19
36
  * Restituisce un array di oggetti { tid, did }.
@@ -40,12 +57,10 @@ export const useWorkflowApprove = () => {
40
57
  }
41
58
  setIsLoading(true);
42
59
  try {
43
- const result = await SDK_Globals.tmSession.NewWorkflowEngine().WFApprAsync(1);
44
- SDK_Globals.currentWorkflowApproveData = result ?? [];
45
- // Aggiorna anche il totale dei dcmtsFound
46
- setTotalDcmtsFound(calculateTotalDcmtsFound(SDK_Globals.currentWorkflowApproveData));
47
- // Notifica tutte le istanze del hook che i dati sono cambiati.
48
- window.dispatchEvent(new CustomEvent('onWorkflowApproveChange'));
60
+ if (!inFlight) {
61
+ inFlight = load().finally(() => { inFlight = undefined; });
62
+ }
63
+ await inFlight;
49
64
  }
50
65
  catch (error) {
51
66
  console.error("Errore durante il fetch dei dati di approvazione workflow:", error);
@@ -53,18 +68,19 @@ export const useWorkflowApprove = () => {
53
68
  finally {
54
69
  setIsLoading(false);
55
70
  }
56
- }, [calculateTotalDcmtsFound]); // useCallback con array vuoto perché non ha dipendenze esterne a React
71
+ }, []);
57
72
  // Questo useEffect serve a sincronizzare lo stato tra diverse istanze del hook.
58
73
  // Se il componente A chiama refreshWorkflowApprove, il componente B (che usa lo stesso hook)
59
74
  // riceverà l'evento e aggiornerà il suo stato locale.
60
75
  useEffect(() => {
61
76
  const handleUpdate = () => {
62
77
  setWorkflowApproveData(SDK_Globals.currentWorkflowApproveData);
78
+ setTotalDcmtsFound(calcTotalDcmtsFound(SDK_Globals.currentWorkflowApproveData));
63
79
  };
64
- window.addEventListener('onWorkflowApproveChange', handleUpdate);
80
+ window.addEventListener(WORKFLOW_APPROVE_CHANGED_EVENT, handleUpdate);
65
81
  return () => {
66
- window.removeEventListener('onWorkflowApproveChange', handleUpdate);
82
+ window.removeEventListener(WORKFLOW_APPROVE_CHANGED_EVENT, handleUpdate);
67
83
  };
68
- }, [calculateTotalDcmtsFound]);
84
+ }, []);
69
85
  return { workflowApproveData, isLoading, refreshWorkflowApprove, totalDcmtsFound, getWorkItemsByDID };
70
86
  };
@@ -103,6 +103,17 @@ export declare class CaseFlowService {
103
103
  * @returns id della nuova istanza.
104
104
  */
105
105
  static CreateCaseFlowInstance(template: CaseFlowTemplateDescriptor, instanceName: string, instanceDescription: string | undefined, metadataValues: Record<number, string>): Promise<number>;
106
+ /**
107
+ * Avvia un'istanza tramite StartAsync(cftid, id): il backend porta la pratica
108
+ * da `New` a `InProgress` e materializza i workitem della prima azione.
109
+ *
110
+ * NON va chiamata dopo `CreateCaseFlowInstance`: la Create dell'API REST
111
+ * esegue già lo start internamente, quindi un'istanza appena creata è
112
+ * ancora `New` solo nel descrittore inviato, non lato server. Questa resta
113
+ * per le istanze che si trovano davvero in `New` (avvio differito); sulle
114
+ * altre il backend rifiuta.
115
+ */
116
+ static startCaseflow(caseFlowId: number, cftid?: number): Promise<void>;
106
117
  /**
107
118
  * Elimina un'istanza tramite DeleteAsync(cftid, id). Il cftid è preso dal
108
119
  * template fornito (se disponibile) oppure risolto dall'istanza.
@@ -190,11 +201,10 @@ export declare class CaseFlowService {
190
201
  /**
191
202
  * Stato "l'utente corrente segue questa pratica" letto dal descrittore.
192
203
  *
193
- * Restituisce `undefined` quando il descrittore non porta l'informazione —
194
- * cioè SEMPRE, finché il backend non aggiunge `IsFollow` in output a
195
- * `usp_Cfi_RetrieveAll`/`usp_Cfi_Retrieve` e la relativa proprietà al
196
- * contratto. Il tipo è tollerante perché la colonna è un BIT/NUMBER e può
197
- * arrivare come booleano, come 0/1 o come stringa.
204
+ * `isFollow` fa ormai parte del contratto (`CaseFlowInstanceDescriptor`):
205
+ * arriva valorizzato da `usp_Cfi_RetrieveAll`/`usp_Cfi_Retrieve`. Resta
206
+ * `undefined` solo quando il descrittore proviene da una sorgente che non
207
+ * popola il campo.
198
208
  *
199
209
  * Da NON dedurre dalla bacheca Home come fanno pratiche-dossier e gruppi di
200
210
  * lavoro (filtro su `HomeBlogPost.classID`): quella query prende `TOP 30`
@@ -213,7 +223,27 @@ export declare class CaseFlowService {
213
223
  static completeDataCompletionWorkItem(caseFlowId: number, workItemId: number, metadataValues: Record<number, string>, response?: string, cftid?: number): Promise<void>;
214
224
  /** Rifiuta un workitem. Il motivo viene pubblicato in bacheca dal backend. */
215
225
  static rejectWorkItem(caseFlowId: number, workItemId: number, response?: string, cftid?: number): Promise<void>;
216
- /** Assegna un'azione a un utente creando il relativo workitem (operazione Assign). */
226
+ /**
227
+ * Prende in carico un'azione per se stessi (operazione Claim): il backend
228
+ * crea il workitem intestato al chiamante e porta l'azione in lavorazione.
229
+ *
230
+ * Non e' un caso particolare di `createWorkItemForAction`: assegnare e'
231
+ * riservato a Owner e Supervisore, prendere in carico e' di chiunque lavori
232
+ * alla pratica. Passando dall'operazione Assign, un Partecipante si vedeva
233
+ * rifiutare la presa in carico (TMC-2789).
234
+ */
235
+ static claimAction(caseFlowId: number, actionID: number, cftid?: number): Promise<void>;
236
+ /**
237
+ * Rilascia un workitem preso in carico (operazione ReleaseClaim): il backend
238
+ * lo cancella e, se per quell'azione non restano altri destinatari, la riporta
239
+ * disponibile per la presa in carico.
240
+ *
241
+ * Stessa chiamata per due gesti diversi, e' la guardia della SP a distinguerli:
242
+ * il detentore rilascia il proprio workitem, un Owner/Supervisore annulla
243
+ * l'assegnazione di un altro. Chi chiama non deve dire quale dei due sta facendo.
244
+ */
245
+ static releaseClaim(caseFlowId: number, workItemId: number, cftid?: number): Promise<void>;
246
+ /** Assegna un'azione a un ALTRO utente creando il relativo workitem (operazione Assign). */
217
247
  static createWorkItemForAction(caseFlowId: number, actionID: number, toUserID: number, description?: string, cftid?: number): Promise<void>;
218
248
  /** Forza il completamento di un'azione (supervisor). */
219
249
  static forceCompleteAction(caseFlowId: number, actionID: number, reason?: string, cftid?: number): Promise<void>;
@@ -221,6 +221,25 @@ export class CaseFlowService {
221
221
  const id = await engine.CreateAsync(d);
222
222
  return id ?? 0;
223
223
  }
224
+ /**
225
+ * Avvia un'istanza tramite StartAsync(cftid, id): il backend porta la pratica
226
+ * da `New` a `InProgress` e materializza i workitem della prima azione.
227
+ *
228
+ * NON va chiamata dopo `CreateCaseFlowInstance`: la Create dell'API REST
229
+ * esegue già lo start internamente, quindi un'istanza appena creata è
230
+ * ancora `New` solo nel descrittore inviato, non lato server. Questa resta
231
+ * per le istanze che si trovano davvero in `New` (avvio differito); sulle
232
+ * altre il backend rifiuta.
233
+ */
234
+ static async startCaseflow(caseFlowId, cftid) {
235
+ const engine = SDK_Globals.tmSession?.NewCaseFlowInstanceEngine();
236
+ if (!engine)
237
+ throw new Error('Motore delle istanze CaseFlow non disponibile.');
238
+ const resolvedCftid = cftid ?? (await this.getCaseflow(caseFlowId))?.cftid;
239
+ if (resolvedCftid == null)
240
+ throw new Error(`Impossibile risolvere il template (cftid) per l'istanza id=${caseFlowId}.`);
241
+ await engine.StartAsync(resolvedCftid, caseFlowId);
242
+ }
224
243
  /**
225
244
  * Elimina un'istanza tramite DeleteAsync(cftid, id). Il cftid è preso dal
226
245
  * template fornito (se disponibile) oppure risolto dall'istanza.
@@ -490,11 +509,10 @@ export class CaseFlowService {
490
509
  /**
491
510
  * Stato "l'utente corrente segue questa pratica" letto dal descrittore.
492
511
  *
493
- * Restituisce `undefined` quando il descrittore non porta l'informazione —
494
- * cioè SEMPRE, finché il backend non aggiunge `IsFollow` in output a
495
- * `usp_Cfi_RetrieveAll`/`usp_Cfi_Retrieve` e la relativa proprietà al
496
- * contratto. Il tipo è tollerante perché la colonna è un BIT/NUMBER e può
497
- * arrivare come booleano, come 0/1 o come stringa.
512
+ * `isFollow` fa ormai parte del contratto (`CaseFlowInstanceDescriptor`):
513
+ * arriva valorizzato da `usp_Cfi_RetrieveAll`/`usp_Cfi_Retrieve`. Resta
514
+ * `undefined` solo quando il descrittore proviene da una sorgente che non
515
+ * popola il campo.
498
516
  *
499
517
  * Da NON dedurre dalla bacheca Home come fanno pratiche-dossier e gruppi di
500
518
  * lavoro (filtro su `HomeBlogPost.classID`): quella query prende `TOP 30`
@@ -503,14 +521,7 @@ export class CaseFlowService {
503
521
  * poi, non esiste nemmeno un ramo nella UNION.
504
522
  */
505
523
  static readFollowFlag(cf) {
506
- const raw = cf.isFollow;
507
- if (typeof raw === 'boolean')
508
- return raw;
509
- if (typeof raw === 'number')
510
- return raw !== 0;
511
- if (raw === '0' || raw === '1')
512
- return raw === '1';
513
- return undefined;
524
+ return cf.isFollow;
514
525
  }
515
526
  // ----- Workitem: movimentazione del flusso -------------------------------
516
527
  //
@@ -551,7 +562,37 @@ export class CaseFlowService {
551
562
  comment: response,
552
563
  }), cftid);
553
564
  }
554
- /** Assegna un'azione a un utente creando il relativo workitem (operazione Assign). */
565
+ /**
566
+ * Prende in carico un'azione per se stessi (operazione Claim): il backend
567
+ * crea il workitem intestato al chiamante e porta l'azione in lavorazione.
568
+ *
569
+ * Non e' un caso particolare di `createWorkItemForAction`: assegnare e'
570
+ * riservato a Owner e Supervisore, prendere in carico e' di chiunque lavori
571
+ * alla pratica. Passando dall'operazione Assign, un Partecipante si vedeva
572
+ * rifiutare la presa in carico (TMC-2789).
573
+ */
574
+ static async claimAction(caseFlowId, actionID, cftid) {
575
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
576
+ operation: CF_WIOperations.Claim,
577
+ actionID,
578
+ }), cftid);
579
+ }
580
+ /**
581
+ * Rilascia un workitem preso in carico (operazione ReleaseClaim): il backend
582
+ * lo cancella e, se per quell'azione non restano altri destinatari, la riporta
583
+ * disponibile per la presa in carico.
584
+ *
585
+ * Stessa chiamata per due gesti diversi, e' la guardia della SP a distinguerli:
586
+ * il detentore rilascia il proprio workitem, un Owner/Supervisore annulla
587
+ * l'assegnazione di un altro. Chi chiama non deve dire quale dei due sta facendo.
588
+ */
589
+ static async releaseClaim(caseFlowId, workItemId, cftid) {
590
+ await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
591
+ operation: CF_WIOperations.ReleaseClaim,
592
+ wiid: workItemId,
593
+ }), cftid);
594
+ }
595
+ /** Assegna un'azione a un ALTRO utente creando il relativo workitem (operazione Assign). */
555
596
  static async createWorkItemForAction(caseFlowId, actionID, toUserID, description, cftid) {
556
597
  await this.executeWorkItemOperation(caseFlowId, CaseFlowWIOperationDescriptor.fromJS({
557
598
  operation: CF_WIOperations.Assign,
@@ -1,3 +1,4 @@
1
+ import React from "react";
1
2
  import { JobTypes, ObjectClasses, ProcessDescriptor, DcmtTypeDescriptor } from "@topconsultnpm/sdk-ts";
2
3
  export declare class PlatformObjectService {
3
4
  static readonly retrieveAllAsync: (objClass: ObjectClasses, refreshCache?: boolean) => Promise<import("@topconsultnpm/sdk-ts").AreaDescriptor[] | import("@topconsultnpm/sdk-ts").NotificationDescriptor[] | import("@topconsultnpm/sdk-ts").RelationDescriptor[] | import("@topconsultnpm/sdk-ts").TaskDescriptor[] | import("@topconsultnpm/sdk-ts").WorkingGroupDescriptor[] | undefined>;
@@ -13,6 +14,6 @@ export declare class PlatformObjectService {
13
14
  static readonly createAsync: (objClass: ObjectClasses, jobType: JobTypes, d: any) => Promise<number | undefined>;
14
15
  static readonly deleteAsync: (objClass: ObjectClasses, id: number) => Promise<number | undefined>;
15
16
  static readonly getName: (objClass: ObjectClasses, jobType?: JobTypes, isPlural?: boolean) => string;
16
- static readonly getIcon: (objClass: ObjectClasses, d: any) => import("react/jsx-runtime").JSX.Element;
17
- static readonly getTooltip: (objClass: ObjectClasses, d: any) => import("react/jsx-runtime").JSX.Element | null;
17
+ static readonly getIcon: (objClass: ObjectClasses, d: any) => React.JSX.Element;
18
+ static readonly getTooltip: (objClass: ObjectClasses, d: any) => React.JSX.Element | null;
18
19
  }