@topconsultnpm/sdkui-react 6.22.0-dev3.24 → 6.22.0-dev3.25

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 (47) hide show
  1. package/lib/components/base/TMAlert.js +6 -8
  2. package/lib/components/base/TMAreaManager.js +53 -55
  3. package/lib/components/base/TMCustomButton.js +39 -48
  4. package/lib/components/base/TMTreeView.js +14 -0
  5. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  6. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  7. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  8. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  9. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
  10. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  11. package/lib/components/base/waitPanel/index.d.ts +1 -0
  12. package/lib/components/base/waitPanel/index.js +1 -0
  13. package/lib/components/choosers/TMPathChooser.js +17 -34
  14. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
  15. package/lib/components/features/documents/TMBatchUpdateForm.js +3 -4
  16. package/lib/components/features/documents/TMCopyToFolderForm.js +32 -35
  17. package/lib/components/features/documents/TMDcmtForm.js +48 -60
  18. package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
  19. package/lib/components/features/documents/TMDcmtIcon.js +3 -5
  20. package/lib/components/features/documents/TMDcmtPreview.js +5 -3
  21. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  22. package/lib/components/features/documents/TMMasterDetailDcmts.js +45 -147
  23. package/lib/components/features/documents/TMMergeToPdfForm.js +48 -47
  24. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +14 -15
  25. package/lib/components/features/documents/TMRelationViewer.d.ts +3 -14
  26. package/lib/components/features/documents/TMRelationViewer.js +115 -79
  27. package/lib/components/features/search/TMSearchResult.js +6 -8
  28. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  29. package/lib/components/features/tasks/TMTasksView.js +91 -90
  30. package/lib/components/features/wg/TMWGsCopyMoveForm.js +91 -91
  31. package/lib/components/grids/TMBlogsPost.js +92 -96
  32. package/lib/components/index.d.ts +2 -0
  33. package/lib/components/index.js +2 -0
  34. package/lib/components/pages/TMPage.js +29 -31
  35. package/lib/helper/SDKUI_Localizator.d.ts +7 -0
  36. package/lib/helper/SDKUI_Localizator.js +70 -0
  37. package/lib/helper/ZipManager.d.ts +1 -1
  38. package/lib/helper/ZipManager.js +2 -1
  39. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  40. package/lib/hooks/useCheckInOutOperations.js +14 -34
  41. package/lib/hooks/useDcmtOperations.d.ts +3 -12
  42. package/lib/hooks/useDcmtOperations.js +130 -160
  43. package/lib/hooks/useDocumentOperations.js +12 -23
  44. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
  45. package/lib/hooks/useMultiMasterDetailDcmts.js +62 -65
  46. package/lib/hooks/useWorkflowApprove.js +33 -17
  47. package/package.json +1 -1
@@ -109,10 +109,10 @@ export const useDocumentOperations = (props) => {
109
109
  // Context helpers
110
110
  const isDcmtFormContext = context === SearchResultContext.DCMT_FORM;
111
111
  const isMasterDetailContext = context === SearchResultContext.MASTER_DETAIL;
112
- const { showHistory, showHistoryCallback, hideHistoryCallback, showCheckoutInformationForm, commentFormState, hideCommentFormCallback, showCheckoutInformationFormCallback, hideCheckoutInformationFormCallback, copyCheckoutPathToClipboardCallback, handleCheckOutCallback, handleCheckInCallback, showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, } = useCheckInOutOperations({
112
+ const { showHistory, showHistoryCallback, hideHistoryCallback, showCheckoutInformationForm, commentFormState, hideCommentFormCallback, showCheckoutInformationFormCallback, hideCheckoutInformationFormCallback, copyCheckoutPathToClipboardCallback, handleCheckOutCallback, handleCheckInCallback, } = useCheckInOutOperations({
113
113
  onRefreshPreview: onRefreshPreviewCallback
114
114
  });
115
- const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync, getDcmtFileAsync, clearDcmtsFileCache, removeDcmtsFileCache, isDcmtFileInCache, FileSourceDialog } = useDcmtOperations();
115
+ const { downloadDcmtsAsync, runOperationAsync, getDcmtFileAsync, clearDcmtsFileCache, removeDcmtsFileCache, isDcmtFileInCache, FileSourceDialog } = useDcmtOperations();
116
116
  const {
117
117
  // Data
118
118
  relatedDcmts, pairedSearchResults, manyToManyRelations, selectedManyToManyRelation, manyToManyChooserDataSource, relatedDcmtsChooserDataSource,
@@ -178,8 +178,14 @@ export const useDocumentOperations = (props) => {
178
178
  const [isOpenDetailsModal, setIsOpenDetailsModal] = useState(false);
179
179
  const [isOpenMasterModal, setIsOpenMasterModal] = useState(false);
180
180
  const [isOpenMultiDetailModal, setIsOpenMultiDetailModal] = useState(false);
181
- const openDetailDcmtsFormHandler = (value) => { setIsOpenDetails(value); };
182
- const openMasterDcmtsFormHandler = (value) => { setIsOpenMaster(value); };
181
+ // La maschera delle correlazioni viene montata subito (parte il calcolo, con il wait panel)
182
+ // ma resa visibile (isOpen...) solo a calcolo finito: intanto sotto restano i risultati.
183
+ const [isMountedDetails, setIsMountedDetails] = useState(false);
184
+ const [isMountedMaster, setIsMountedMaster] = useState(false);
185
+ const openDetailDcmtsFormHandler = (value) => { setIsMountedDetails(value); if (!value)
186
+ setIsOpenDetails(false); };
187
+ const openMasterDcmtsFormHandler = (value) => { setIsMountedMaster(value); if (!value)
188
+ setIsOpenMaster(false); };
183
189
  const openDetailDcmtsModalHandler = (value) => { setIsOpenDetailsModal(value); };
184
190
  const openMasterDcmtsModalHandler = (value) => { setIsOpenMasterModal(value); };
185
191
  // State to control whether the export form (for exporting to Excel/CSV/txt and others) should be shown
@@ -1413,8 +1419,8 @@ export const useDocumentOperations = (props) => {
1413
1419
  onRefreshBlogDatagrid,
1414
1420
  onRefreshPreviewDatagrid
1415
1421
  }, fetchRemoteCertificates: fetchRemoteCertificates, graphometricManager: graphometricManager })) }), (showHistory && dtd && selectedDcmtInfos.length > 0) && _jsx(TMViewHistoryDcmt, { fromDTD: dtd, deviceType: deviceType, inputDcmt: selectedDcmtInfos[0], onClose: hideHistoryCallback, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), (commentFormState.show && selectedDcmtInfos.length > 0) && _jsx(TMBlogCommentForm, { context: { engine: 'SearchEngine', object: { tid: selectedDcmtInfos[0].TID, did: selectedDcmtInfos[0].DID } }, onClose: hideCommentFormCallback, refreshCallback: onRefreshBlog, participants: [], showAttachmentsSection: true, allArchivedDocumentsFileItems: convertSearchResultDescriptorToFileItems(currentSearchResults ?? []), isCommentRequired: commentFormState.isRequired, removeAndEditAttachment: commentFormState.removeAndEditAttachment, selectedAttachmentDid: [Number(selectedDcmtInfos[0].DID)] }), (showCheckoutInformationForm && dtd && selectedDcmtInfos.length > 0) &&
1416
- _jsx(TMDcmtCheckoutInfoForm, { dtdName: dtd.name ?? SDKUI_Localizator.SearchResult, selectedDcmtOrFocused: selectedDcmtInfos[0], onClose: hideCheckoutInformationFormCallback }), isOpenDetailsModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenDetailsModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailDetailsCommonProps, onBack: () => setIsOpenDetailsModal(false) }) }), _jsx(StyledMultiViewPanel, { $isVisible: isOpenDetails, children: isOpenDetails && _jsx(TMMasterDetailDcmts, { ...masterDetailDetailsCommonProps, onBack: () => setIsOpenDetails(false) }) }), (isOpenMultiDetailModal && dtd) &&
1417
- _jsx(TMMultiMasterDetailDcmtsForm, { dtd: dtd, masterDcmt: selectedDcmtInfos[0], onClose: () => setIsOpenMultiDetailModal(false), canNavigateMaster: canNavigateMultiDetailMaster ? canNavigateHandler : undefined, onNavigateMaster: canNavigateMultiDetailMaster ? onNavigateHandler : undefined, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showDcmtFormSidebar: showDcmtFormSidebar, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm }), isOpenMasterModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenMasterModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMasterModal(false) }) }), _jsxs(StyledMultiViewPanel, { $isVisible: isOpenMaster, children: [isOpenMaster && _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMaster(false) }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
1422
+ _jsx(TMDcmtCheckoutInfoForm, { dtdName: dtd.name ?? SDKUI_Localizator.SearchResult, selectedDcmtOrFocused: selectedDcmtInfos[0], onClose: hideCheckoutInformationFormCallback }), isOpenDetailsModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenDetailsModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailDetailsCommonProps, onBack: () => setIsOpenDetailsModal(false) }) }), _jsx(StyledMultiViewPanel, { $isVisible: isOpenDetails, children: isMountedDetails && _jsx(TMMasterDetailDcmts, { ...masterDetailDetailsCommonProps, onFirstLoadCompleted: () => setIsOpenDetails(true), onBack: () => openDetailDcmtsFormHandler(false) }) }), (isOpenMultiDetailModal && dtd) &&
1423
+ _jsx(TMMultiMasterDetailDcmtsForm, { dtd: dtd, masterDcmt: selectedDcmtInfos[0], onClose: () => setIsOpenMultiDetailModal(false), canNavigateMaster: canNavigateMultiDetailMaster ? canNavigateHandler : undefined, onNavigateMaster: canNavigateMultiDetailMaster ? onNavigateHandler : undefined, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showDcmtFormSidebar: showDcmtFormSidebar, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm }), isOpenMasterModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenMasterModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMasterModal(false) }) }), _jsxs(StyledMultiViewPanel, { $isVisible: isOpenMaster, children: [isMountedMaster && _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onFirstLoadCompleted: () => setIsOpenMaster(true), onBack: () => openMasterDcmtsFormHandler(false) }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
1418
1424
  return (_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, inputDcmts: [dcmt], allowNavigation: false, onBack: () => handleRemoveItem(dcmt.TID, dcmt.DID) }) }, `${index}-${dcmt.DID}`));
1419
1425
  })] }), isOpenArchiveRelationForm && _jsx(TMDcmtForm, { isModal: true, titleModal: SDKUI_Localizator.Archive + ' - ' + (archiveType === 'detail' ? SDKUI_Localizator.DcmtsDetail : SDKUI_Localizator.DcmtsMaster), TID: archiveRelatedDcmtFormTID, layoutMode: LayoutModes.Ark, inputMids: archiveRelatedDcmtFormMids, showBackButton: false, allowButtonsRefs: false, onClose: () => {
1420
1426
  setIsOpenArchiveRelationForm(false);
@@ -1513,25 +1519,8 @@ export const useDocumentOperations = (props) => {
1513
1519
  copyCheckoutPathToClipboardCallback,
1514
1520
  handleCheckOutCallback,
1515
1521
  handleCheckInCallback,
1516
- showCicoWaitPanel,
1517
- cicoWaitPanelTitle,
1518
- showCicoPrimaryProgress,
1519
- cicoPrimaryProgressText,
1520
- cicoPrimaryProgressValue,
1521
- cicoPrimaryProgressMax,
1522
1522
  },
1523
1523
  dcmtOperations: {
1524
- abortController,
1525
- showWaitPanel,
1526
- showPrimary,
1527
- waitPanelTitle,
1528
- waitPanelTextPrimary,
1529
- waitPanelValuePrimary,
1530
- waitPanelMaxValuePrimary,
1531
- showSecondary,
1532
- waitPanelTextSecondary,
1533
- waitPanelValueSecondary,
1534
- waitPanelMaxValueSecondary,
1535
1524
  downloadDcmtsAsync,
1536
1525
  getDcmtFileAsync,
1537
1526
  clearDcmtsFileCache,
@@ -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
  };
@@ -5,6 +5,7 @@ import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDet
5
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,8 +65,6 @@ 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);
@@ -620,44 +607,68 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
620
607
  dataTypeByMid.set(md.id, md.dataType); });
621
608
  // Collegamenti della relazione valorizzati dal master (prevalgono sui valori del form)
622
609
  const associationMids = await getMasterAssociationsForDetailAsync(selectedRelation);
623
- // Barra primaria: avanzamento documenti. Barra secondaria: upload del file corrente
624
- const abortController = new AbortController();
625
- abortControllerRef.current = abortController;
626
- setWaitPanel({
627
- ...CLOSED_WAIT_PANEL,
628
- showWaitPanel: true,
629
- waitPanelTitle: SDKUI_Localizator.Archiving,
630
- showWaitPanelPrimary: rowsToArchive.length > 1,
631
- waitPanelMaxValuePrimary: rowsToArchive.length,
632
- 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,
633
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
+ };
634
630
  const result = [];
635
631
  const archivedRowIds = new Set();
636
- for (let i = 0; i < rowsToArchive.length; i++) {
637
- if (abortController.signal.aborted) {
638
- result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
639
- break;
640
- }
641
- const rowData = rowsToArchive[i];
642
- try {
643
- setWaitPanel(prev => ({ ...prev, waitPanelTextPrimary: `${SDKUI_Localizator.Archiving} ${i + 1} / ${rowsToArchive.length}` }));
644
- const newDID = await archiveRowAsync(rowData, detailTID, dataTypeByMid, associationMids, abortController.signal);
645
- setWaitPanel(prev => ({ ...prev, waitPanelValuePrimary: i + 1 }));
646
- archivedRowIds.add(rowData.id);
647
- result.push({ rowIndex: i, id1: detailTID, id2: newDID, resultType: ResultTypes.SUCCESS });
648
- }
649
- catch (error) {
650
- const err = error;
651
- // Annullamento richiesto dall'utente: interrompe senza mostrare l'errore
652
- 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) {
653
637
  result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
638
+ waitPanel.warning();
654
639
  break;
655
640
  }
656
- result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.ERROR, description: getExceptionMessage(error) });
657
- if (stopOnFirstError)
658
- 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();
659
666
  }
660
667
  }
668
+ finally {
669
+ // Chiuso prima del riepilogo dei risultati
670
+ waitPanel.close();
671
+ }
661
672
  // Le righe archiviate escono dal riepilogo
662
673
  if (archivedRowIds.size > 0) {
663
674
  setArchiveRows(prev => prev.filter(r => !archivedRowIds.has(r.id)));
@@ -677,13 +688,11 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
677
688
  TMExceptionBoxManager.show({ exception: error });
678
689
  }
679
690
  finally {
680
- abortControllerRef.current = undefined;
681
- setWaitPanel(CLOSED_WAIT_PANEL);
682
691
  setIsArchiving(false);
683
692
  }
684
693
  };
685
- // Archiviazione di una singola riga, con avanzamento dell'upload sulla barra secondaria
686
- 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) => {
687
696
  const ae = new ArchiveEngineByID(SDK_Globals.tmSession);
688
697
  ae.TID = detailTID;
689
698
  ae.Metadata_ClearAll();
@@ -715,23 +724,12 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
715
724
  });
716
725
  if (row.__file)
717
726
  ae.ArchivingFile = row.__file;
718
- let firstBlock = true;
719
- let maxFileSize = 0;
720
727
  return ae.ArchiveAsync(signal, (pd) => {
721
- if (firstBlock) {
722
- maxFileSize = pd.ProgressBarMaximum ?? 0;
723
- setWaitPanel(prev => ({ ...prev, waitPanelMaxValueSecondary: maxFileSize }));
724
- firstBlock = false;
725
- }
726
- setWaitPanel(prev => ({
727
- ...prev,
728
- waitPanelValueSecondary: pd.ProgressBarValue,
729
- waitPanelTextSecondary: `Uploading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`,
730
- }));
731
- if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
732
- setWaitPanel(prev => ({ ...prev, waitPanelValueSecondary: 0, waitPanelMaxValueSecondary: 0, waitPanelTextSecondary: '' }));
733
- firstBlock = true;
734
- }
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);
735
733
  });
736
734
  };
737
735
  return {
@@ -756,6 +754,5 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigat
756
754
  openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, copyArchivedDetailToRows,
757
755
  // Archiviazione
758
756
  stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync,
759
- waitPanel, abortController: abortControllerRef.current,
760
757
  };
761
758
  };
@@ -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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.22.0-dev3.24",
3
+ "version": "6.22.0-dev3.25",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",