@topconsultnpm/sdkui-react 6.22.0-dev3.9 → 6.22.0-dev4.3

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 (96) 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/TMButton.js +4 -2
  4. package/lib/components/base/TMCustomButton.js +40 -48
  5. package/lib/components/base/TMDataGrid.js +219 -219
  6. package/lib/components/base/TMPanel.js +173 -170
  7. package/lib/components/base/TMSpinner.d.ts +2 -1
  8. package/lib/components/base/TMSpinner.js +9 -7
  9. package/lib/components/base/TMTabControl.d.ts +5 -0
  10. package/lib/components/base/TMTabControl.js +18 -7
  11. package/lib/components/base/TMTreeView.js +20 -0
  12. package/lib/components/base/TMWaitPanel.js +1 -1
  13. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  14. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  15. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
  16. package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
  17. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
  18. package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
  19. package/lib/components/base/waitPanel/index.d.ts +1 -0
  20. package/lib/components/base/waitPanel/index.js +1 -0
  21. package/lib/components/choosers/TMPathChooser.js +17 -34
  22. package/lib/components/editors/TMTextBox.d.ts +4 -0
  23. package/lib/components/editors/TMTextBox.js +99 -11
  24. package/lib/components/features/archive/TMArchive.js +7 -2
  25. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +29 -0
  26. package/lib/components/features/assistant/TMHelpOnLineChat.js +1384 -0
  27. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
  28. package/lib/components/features/assistant/dataset/Designer-Suggested-Questions.json +102 -0
  29. package/lib/components/features/assistant/dataset/Login-Suggested-Questions.json +32 -0
  30. package/lib/components/features/assistant/dataset/Surfer-Suggested-Questions.json +102 -0
  31. package/lib/components/features/documents/TMBatchUpdateForm.js +3 -4
  32. package/lib/components/features/documents/TMCopyToFolderForm.js +53 -84
  33. package/lib/components/features/documents/TMDcmtForm.js +50 -61
  34. package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
  35. package/lib/components/features/documents/TMDcmtIcon.js +3 -5
  36. package/lib/components/features/documents/TMDcmtPreview.js +5 -3
  37. package/lib/components/features/documents/TMDcmtTasks.js +2 -9
  38. package/lib/components/features/documents/TMFileUploader.js +26 -26
  39. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  40. package/lib/components/features/documents/TMMasterDetailDcmts.js +53 -148
  41. package/lib/components/features/documents/TMMasterInfoFields.js +3 -2
  42. package/lib/components/features/documents/TMMergeToPdfForm.js +49 -54
  43. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +14 -15
  44. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +1 -1
  45. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +3 -3
  46. package/lib/components/features/documents/TMRelationViewer.d.ts +13 -14
  47. package/lib/components/features/documents/TMRelationViewer.js +206 -143
  48. package/lib/components/features/documents/relationTreeState.d.ts +32 -0
  49. package/lib/components/features/documents/relationTreeState.js +89 -0
  50. package/lib/components/features/search/TMSearch.js +3 -4
  51. package/lib/components/features/search/TMSearchQueryPanel.js +0 -4
  52. package/lib/components/features/search/TMSearchResult.d.ts +5 -0
  53. package/lib/components/features/search/TMSearchResult.js +39 -19
  54. package/lib/components/features/search/TMTreeSelector.js +23 -27
  55. package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
  56. package/lib/components/features/tasks/TMTasksView.js +91 -90
  57. package/lib/components/features/wg/TMWGsCopyMoveForm.js +91 -91
  58. package/lib/components/forms/TMResultDialog.js +1 -1
  59. package/lib/components/grids/TMBlogsPost.js +92 -96
  60. package/lib/components/index.d.ts +5 -1
  61. package/lib/components/index.js +4 -1
  62. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +16 -15
  63. package/lib/components/layout/panelManager/TMPanelManagerContext.d.ts +1 -0
  64. package/lib/components/layout/panelManager/TMPanelManagerContext.js +5 -1
  65. package/lib/components/layout/panelManager/TMPanelManagerLoader.d.ts +6 -0
  66. package/lib/components/layout/panelManager/TMPanelManagerLoader.js +18 -0
  67. package/lib/components/layout/panelManager/TMPanelManagerToolbar.js +4 -8
  68. package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.d.ts +1 -1
  69. package/lib/components/layout/panelManager/TMPanelManagerWithPersistenceProvider.js +2 -1
  70. package/lib/components/pages/TMPage.js +29 -31
  71. package/lib/components/query/TMQueryEditor.d.ts +3 -0
  72. package/lib/components/query/TMQueryEditor.js +2 -1
  73. package/lib/components/sidebar/TMHeader.d.ts +1 -0
  74. package/lib/components/sidebar/TMHeader.js +2 -2
  75. package/lib/helper/Enum_Localizator.js +1 -0
  76. package/lib/helper/SDKUI_Globals.d.ts +3 -0
  77. package/lib/helper/SDKUI_Globals.js +3 -0
  78. package/lib/helper/SDKUI_Localizator.d.ts +21 -0
  79. package/lib/helper/SDKUI_Localizator.js +210 -0
  80. package/lib/helper/ZipManager.d.ts +1 -1
  81. package/lib/helper/ZipManager.js +2 -1
  82. package/lib/helper/dcmtsHelper.d.ts +2 -0
  83. package/lib/helper/dcmtsHelper.js +3 -1
  84. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  85. package/lib/hooks/useCheckInOutOperations.js +14 -34
  86. package/lib/hooks/useDcmtOperations.d.ts +3 -19
  87. package/lib/hooks/useDcmtOperations.js +160 -222
  88. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  89. package/lib/hooks/useDocumentOperations.js +14 -25
  90. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
  91. package/lib/hooks/useMultiMasterDetailDcmts.js +65 -67
  92. package/lib/hooks/usePreventFileDrop.js +9 -0
  93. package/lib/hooks/useWorkflowApprove.js +33 -17
  94. package/lib/services/caseflow/CaseFlowService.d.ts +36 -6
  95. package/lib/services/caseflow/CaseFlowService.js +55 -14
  96. package/package.json +69 -69
@@ -1,7 +1,7 @@
1
- import { useState } from 'react';
1
+ import { useEffect, useRef } from 'react';
2
2
  import { LocalizeDcmtOperationTypes } from '../helper/Enum_Localizator';
3
3
  import { SDK_Globals, RetrieveFileOptions, DcmtOpers, ResultTypes, RecentCategories, MetadataDataTypes, MetadataDataDomains, DataListCacheService, DcmtTypeListCacheService, TemplateTIDs } from '@topconsultnpm/sdk-ts';
4
- import { ShowAlert, TMResultManager, FormulaHelper, TMExceptionBoxManager, TMSpinner } from '../components';
4
+ import { ShowAlert, TMResultManager, FormulaHelper, TMExceptionBoxManager, TMSpinner, TMWaitPanelManager } from '../components';
5
5
  import { Globalization, getExceptionMessage, dialogConfirmOperation, extensionHandler, downloadBase64File, SDKUI_Globals, dcmtsFileCacheDownload, CACHE_SIZE_LIMIT, clearDcmtsFileCache, dcmtsFileCachePreview, isDcmtFileInCache, removeDcmtsFileCache, SDKUI_Localizator, ZipManager } from '../helper';
6
6
  import { DcmtOperationTypes, DownloadTypes, FileExtensionHandler } from '../ts';
7
7
  import { useFileDialog, useFileSourceDialog } from './useInputDialog';
@@ -9,10 +9,6 @@ import { importCertificatesAsync } from '../helper/certificateImportHelper';
9
9
  import { isXMLFileExt } from '../helper/dcmtsHelper';
10
10
  import { ShowConfirm } from '../components/base/TMConfirm';
11
11
  import { generateTargetFileName } from '../components/features/documents/copyAndMergeDcmtsShared';
12
- /** Limite massimo di download paralleli accettato da `downloadDcmtsAsync`. */
13
- const MAX_DOWNLOAD_CONCURRENCY = 8;
14
- /** Intervallo minimo (ms) fra due aggiornamenti della progress bar di download. */
15
- const DOWNLOAD_PROGRESS_THROTTLE_MS = 100;
16
12
  const isScannerLicenseConfigured = () => {
17
13
  try {
18
14
  const scannerLicense = SDKUI_Globals.userSettings.advancedSettings.scannerLicense;
@@ -111,16 +107,22 @@ const getDownloadFileName = (file, dcmtInfo, useInputFileName = false) => {
111
107
  return `${baseFileName.slice(0, firstDot)}(${count})${baseFileName.slice(firstDot)}`;
112
108
  };
113
109
  export const useDcmtOperations = () => {
114
- const [showWaitPanel, setShowWaitPanel] = useState(false);
115
- const [waitPanelTitle, setWaitPanelTitle] = useState('');
116
- const [showPrimary, setShowPrimary] = useState(false);
117
- const [waitPanelTextPrimary, setWaitPanelTextPrimary] = useState('');
118
- const [waitPanelValuePrimary, setWaitPanelValuePrimary] = useState(0);
119
- const [waitPanelMaxValuePrimary, setWaitPanelMaxValuePrimary] = useState(0);
120
- const [showSecondary, setShowSecondary] = useState(false);
121
- const [waitPanelTextSecondary, setWaitPanelTextSecondary] = useState('');
122
- const [waitPanelValueSecondary, setWaitPanelValueSecondary] = useState(0);
123
- const [waitPanelMaxValueSecondary, setWaitPanelMaxValueSecondary] = useState(0);
110
+ // Pannello imperativo: l'avanzamento non passa da uno stato React.
111
+ const waitPanelRef = useRef(null);
112
+ /** Apre il wait panel dell'operazione. Interrompi agisce sull'abortController del ciclo. */
113
+ const openWaitPanel = (options) => {
114
+ waitPanelRef.current?.close();
115
+ const panel = TMWaitPanelManager.show({ cancelable: true, onAbort: () => abortController.abort(), ...options });
116
+ waitPanelRef.current = panel;
117
+ return panel;
118
+ };
119
+ const closeWaitPanel = (panel) => {
120
+ panel.close();
121
+ if (waitPanelRef.current === panel)
122
+ waitPanelRef.current = null;
123
+ };
124
+ // Il pannello vive fuori dall'albero del chiamante: allo smontaggio va chiuso a mano.
125
+ useEffect(() => () => { waitPanelRef.current?.close(); waitPanelRef.current = null; }, []);
124
126
  const { OpenFileDialog } = useFileDialog();
125
127
  const [selectFileSource, FileSourceDialog] = useFileSourceDialog();
126
128
  const copyCompliantDcmtsAsync = async (inputDcmt, onFileDownloaded) => {
@@ -133,8 +135,14 @@ export const useDcmtOperations = () => {
133
135
  let returnDcmtInfos = [];
134
136
  const rfo = new RetrieveFileOptions();
135
137
  rfo.retrieveReason = DcmtOpers.ShowFile;
136
- let firstBlock = true;
137
- let maxFileSize = 0;
138
+ // Gira dentro runOperationAsync: i byte vanno sulla barra secondaria del pannello gia' aperto.
139
+ const publishSecondary = (pd, label) => {
140
+ const max = pd.ProgressBarMaximum ?? 0;
141
+ const completed = pd.ProgressBarValue === pd.ProgressBarMaximum;
142
+ waitPanelRef.current?.setSecondary(completed
143
+ ? { value: 0, max: 0, text: '' }
144
+ : { value: pd.ProgressBarValue, max, text: `${label} ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(max, true)}` });
145
+ };
138
146
  let signerInfoDescriptor = undefined;
139
147
  if (inputDcmt?.FILEEXT == undefined || inputDcmt.FILEEXT == '') {
140
148
  throw new Error('WARNING#' + SDKUI_Localizator.MetadataOnlyDocument);
@@ -183,21 +191,7 @@ export const useDcmtOperations = () => {
183
191
  else
184
192
  throw new Error('IdC non firmato e non marcato temporalmente!');
185
193
  //Ricerca dell'impronta SHA256 nel contenuto dell'IdC
186
- let fileIdC = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(serachResultIdC.dtdResult.rows[0][0], serachResultIdC.dtdResult.rows[0][1], rfo, abortController.signal, (pd) => {
187
- if (firstBlock) {
188
- maxFileSize = pd.ProgressBarMaximum ?? 0;
189
- setWaitPanelMaxValueSecondary(maxFileSize);
190
- firstBlock = false;
191
- }
192
- setWaitPanelValueSecondary(pd.ProgressBarValue);
193
- setWaitPanelTextSecondary(`Downloading IdC... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`);
194
- if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
195
- setWaitPanelMaxValueSecondary(0);
196
- setWaitPanelValueSecondary(0);
197
- setWaitPanelTextSecondary('');
198
- firstBlock = true;
199
- }
200
- });
194
+ let fileIdC = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(serachResultIdC.dtdResult.rows[0][0], serachResultIdC.dtdResult.rows[0][1], rfo, abortController.signal, (pd) => publishSecondary(pd, 'Downloading IdC...'));
201
195
  let hashOk = false;
202
196
  if (fileIdC) {
203
197
  try {
@@ -214,21 +208,7 @@ export const useDcmtOperations = () => {
214
208
  throw new Error('Impronta non trovata nel file IdC o non corrispondente a quella del documento');
215
209
  }
216
210
  let file;
217
- file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(inputDcmt.TID, inputDcmt.DID, rfo, abortController.signal, (pd) => {
218
- if (firstBlock) {
219
- maxFileSize = pd.ProgressBarMaximum ?? 0;
220
- setWaitPanelMaxValueSecondary(maxFileSize);
221
- firstBlock = false;
222
- }
223
- setWaitPanelValueSecondary(pd.ProgressBarValue);
224
- setWaitPanelTextSecondary(`Downloading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`);
225
- if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
226
- setWaitPanelMaxValueSecondary(0);
227
- setWaitPanelValueSecondary(0);
228
- setWaitPanelTextSecondary('');
229
- firstBlock = true;
230
- }
231
- });
211
+ file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(inputDcmt.TID, inputDcmt.DID, rfo, abortController.signal, (pd) => publishSecondary(pd, 'Downloading...'));
232
212
  if (file && fileIdC) {
233
213
  returnDcmtFiles.push(file);
234
214
  returnDcmtInfos.push(inputDcmt);
@@ -261,7 +241,7 @@ export const useDcmtOperations = () => {
261
241
  ShowAlert({ message: SDKUI_Localizator.ZipCreatedSavedInFolder.replaceParams(zipFileName, "Zip creato con successo"), mode: 'success', duration: 5000, title: SDKUI_Localizator.CompressToZipFile });
262
242
  }
263
243
  };
264
- const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false, downloadConcurrency = 1) => {
244
+ const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false) => {
265
245
  if (inputDcmts === undefined)
266
246
  return;
267
247
  if (inputDcmts.length <= 0)
@@ -285,86 +265,61 @@ export const useDcmtOperations = () => {
285
265
  }
286
266
  }
287
267
  let operationTitle = 'Download File';
288
- setShowWaitPanel(true);
289
- setShowPrimary(inputDcmts.length > 1);
290
- setShowSecondary(true);
291
- setWaitPanelTitle(operationTitle);
292
268
  abortController = new AbortController();
293
- // Riferimento const: il narrowing su inputDcmts non sopravvive dentro le closure del pool.
269
+ // Riferimento const: il narrowing su inputDcmts non sopravvive dentro le closure.
294
270
  const dcmts = inputDcmts;
295
- /**
296
- * Ogni richiesta usa la propria istanza di RetrieveFileOptions: con il pool più
297
- * download sono in volo contemporaneamente e non possono condividere lo stesso oggetto.
298
- */
299
- const buildRetrieveFileOptions = (dcmt) => {
300
- const rfo = new RetrieveFileOptions();
301
- rfo.retrieveReason = retrieveOptions?.retrieveReason ?? DcmtOpers.ShowFile;
302
- if (retrieveOptions?.cvtFormat !== undefined)
303
- rfo.cvtFormat = retrieveOptions.cvtFormat;
304
- if (retrieveOptions?.generalRetrieveFormat !== undefined)
305
- rfo.generalRetrieveFormat = retrieveOptions.generalRetrieveFormat;
306
- rfo.invoiceRetrieveFormat = retrieveOptions?.invoiceRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
307
- rfo.orderRetrieveFormat = retrieveOptions?.orderRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
308
- return rfo;
309
- };
310
- const results = new Array(dcmts.length);
311
- setWaitPanelMaxValuePrimary(dcmts.length);
312
- // ---- Progress secondaria: aggregata sui download in volo e throttlata ----
313
- // Gli eventi di avanzamento di axios arrivano a ogni chunk: senza throttle ognuno
314
- // provoca un re-render dell'intera form (relation viewer compreso).
315
- const inFlightBytes = new Map();
316
- let pendingSecondary;
317
- let secondaryTimer;
318
- const flushSecondaryProgress = () => {
319
- secondaryTimer = undefined;
320
- if (!pendingSecondary)
321
- return;
322
- setWaitPanelMaxValueSecondary(pendingSecondary.max);
323
- setWaitPanelValueSecondary(pendingSecondary.value);
324
- setWaitPanelTextSecondary(pendingSecondary.text);
325
- pendingSecondary = undefined;
326
- };
327
- const publishSecondaryProgress = () => {
328
- let loaded = 0;
329
- let total = 0;
330
- for (const bytes of inFlightBytes.values()) {
331
- loaded += bytes.loaded;
332
- total += bytes.total;
333
- }
334
- pendingSecondary = total > 0
335
- ? { value: loaded, max: total, text: `Downloading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}` }
336
- : { value: 0, max: 0, text: '' };
337
- if (secondaryTimer === undefined)
338
- secondaryTimer = setTimeout(flushSecondaryProgress, DOWNLOAD_PROGRESS_THROTTLE_MS);
271
+ // Con un solo documento c'e' un avanzamento solo, quello dei byte, e sta sulla barra
272
+ // principale: la doppia barra (documenti + byte) serve solo all'elaborazione multipla.
273
+ const isBatch = dcmts.length > 1;
274
+ const panel = openWaitPanel({
275
+ title: operationTitle,
276
+ max: isBatch ? dcmts.length : 0,
277
+ showSecondary: isBatch,
278
+ showCounters: isBatch,
279
+ primaryLabel: isBatch ? SDKUI_Localizator.Documents : undefined,
280
+ secondaryLabel: isBatch ? 'File corrente' : undefined,
281
+ });
282
+ const rfo = new RetrieveFileOptions();
283
+ rfo.retrieveReason = retrieveOptions?.retrieveReason ?? DcmtOpers.ShowFile;
284
+ if (retrieveOptions?.cvtFormat !== undefined)
285
+ rfo.cvtFormat = retrieveOptions.cvtFormat;
286
+ if (retrieveOptions?.generalRetrieveFormat !== undefined)
287
+ rfo.generalRetrieveFormat = retrieveOptions.generalRetrieveFormat;
288
+ rfo.invoiceRetrieveFormat = retrieveOptions?.invoiceRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
289
+ rfo.orderRetrieveFormat = retrieveOptions?.orderRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
290
+ const results = [];
291
+ // Il pannello coalesce le scritture in un flush per frame: nessun throttle serve.
292
+ const publishBytes = (loaded, total) => {
293
+ const text = total > 0
294
+ ? `Downloading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}`
295
+ : '';
296
+ if (isBatch)
297
+ panel.setSecondary({ value: loaded, max: total, text });
298
+ else
299
+ panel.update({ value: loaded, max: total, subtitle: text });
339
300
  };
340
- // ---- Pool di download ----
341
- let nextIndex = 0;
342
- let started = 0;
301
+ // ---- Download sequenziale: un documento per volta ----
343
302
  let completed = 0;
344
- let abortReported = false;
345
303
  const downloadOne = async (i) => {
346
- started++;
347
- setWaitPanelTextPrimary(`Download file ${started} di ${dcmts.length}`);
304
+ if (isBatch)
305
+ panel.setSubtitle(`Download file ${i + 1} di ${dcmts.length}`);
348
306
  let file;
349
307
  const cacheKey = `${dcmts[i].TID}-${dcmts[i].DID}`;
350
308
  if (useCache && dcmtsFileCacheDownload.has(cacheKey)) {
351
309
  file = dcmtsFileCacheDownload.get(cacheKey);
352
310
  }
353
311
  else {
354
- inFlightBytes.set(i, { loaded: 0, total: 0 });
355
312
  try {
356
- file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(dcmts[i].TID, dcmts[i].DID, buildRetrieveFileOptions(dcmts[i]), abortController.signal, (pd) => {
357
- inFlightBytes.set(i, { loaded: pd.ProgressBarValue, total: pd.ProgressBarMaximum ?? 0 });
358
- publishSecondaryProgress();
359
- });
313
+ file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(dcmts[i].TID, dcmts[i].DID, rfo, abortController.signal, (pd) => publishBytes(pd.ProgressBarValue, pd.ProgressBarMaximum ?? 0));
360
314
  }
361
315
  finally {
362
- inFlightBytes.delete(i);
363
- publishSecondaryProgress();
316
+ if (isBatch)
317
+ publishBytes(0, 0);
364
318
  }
365
319
  }
366
320
  completed++;
367
- setWaitPanelValuePrimary(completed);
321
+ if (isBatch)
322
+ panel.setValue(completed);
368
323
  if (downloadMode === "openInNewWindow") {
369
324
  if (onFileDownloaded && file) {
370
325
  onFileDownloaded(file, dcmts[i]);
@@ -400,48 +355,32 @@ export const useDcmtOperations = () => {
400
355
  }
401
356
  dcmtsFileCacheDownload.set(cacheKey, file);
402
357
  }
403
- results[i] = { rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.SUCCESS };
358
+ results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.SUCCESS });
404
359
  };
405
- const runWorker = async () => {
406
- while (true) {
407
- const i = nextIndex++;
408
- if (i >= dcmts.length)
409
- return;
410
- if (abortController.signal.aborted) {
411
- // Un solo avviso di interruzione anche quando più worker si fermano insieme.
412
- if (!abortReported) {
413
- abortReported = true;
414
- results[i] = { rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${completed} documenti` };
415
- }
416
- return;
417
- }
418
- try {
419
- await downloadOne(i);
420
- }
421
- catch (ex) {
422
- results[i] = { rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(ex) };
423
- const err = ex;
424
- if (err.name === 'CanceledError') {
425
- ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
426
- }
360
+ for (let i = 0; i < dcmts.length; i++) {
361
+ // Con la conferma di interruzione aperta il ciclo si ferma qui invece di
362
+ // continuare a scaricare dietro al dialogo.
363
+ await panel.waitWhilePaused();
364
+ if (abortController.signal.aborted) {
365
+ results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${completed} documenti` });
366
+ panel.warning();
367
+ break;
368
+ }
369
+ try {
370
+ await downloadOne(i);
371
+ panel.ok();
372
+ }
373
+ catch (ex) {
374
+ results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(ex) });
375
+ panel.error();
376
+ const err = ex;
377
+ if (err.name === 'CanceledError') {
378
+ ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
427
379
  }
428
380
  }
429
- };
430
- // `|| 1` intercetta NaN/0/negativi: senza, Array.from({length: NaN}) non creerebbe
431
- // alcun worker e il download terminerebbe in silenzio senza scaricare nulla.
432
- const workers = Math.max(1, Math.min(Math.trunc(downloadConcurrency) || 1, MAX_DOWNLOAD_CONCURRENCY, dcmts.length));
433
- await Promise.all(Array.from({ length: workers }, () => runWorker()));
434
- if (secondaryTimer !== undefined)
435
- clearTimeout(secondaryTimer);
436
- const result = results.filter((r) => r !== undefined);
437
- setWaitPanelTextPrimary('');
438
- setWaitPanelMaxValuePrimary(0);
439
- setWaitPanelValuePrimary(0);
440
- setWaitPanelTextSecondary('');
441
- setWaitPanelMaxValueSecondary(0);
442
- setWaitPanelValueSecondary(0);
443
- setShowWaitPanel(false);
444
- TMResultManager.show(result, operationTitle, "TID", "DID", undefined, undefined, showSuccessAlert);
381
+ }
382
+ closeWaitPanel(panel);
383
+ TMResultManager.show(results, operationTitle, "TID", "DID", undefined, undefined, showSuccessAlert);
445
384
  };
446
385
  const _downloadAttachmentsAsync = async (inputDcmts, confirmAttachments, showSuccessAlert = true) => {
447
386
  if (inputDcmts === undefined)
@@ -505,13 +444,12 @@ export const useDcmtOperations = () => {
505
444
  };
506
445
  const downloadDcmtsAsync = async (params) => {
507
446
  const { inputDcmts, downloadType = DownloadTypes.Attachment, downloadMode = "download", onFileDownloaded, confirmAttachments, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false, // Default: usa il nome dal backend
508
- downloadConcurrency = 1, // Default: sequenziale
509
447
  } = params;
510
448
  switch (downloadType) {
511
449
  // Per il download di documenti, passa useInputFileName per controllare la priorità del nome
512
- case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName, downloadConcurrency);
450
+ case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName);
513
451
  case DownloadTypes.Attachment: return await _downloadAttachmentsAsync(inputDcmts, confirmAttachments, showSuccessAlert);
514
- default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName, downloadConcurrency);
452
+ default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName);
515
453
  }
516
454
  };
517
455
  const uploadDcmtsAsync = async (inputDcmts, operationTitle, operType, actionAfterOperationAsync) => {
@@ -576,22 +514,37 @@ export const useDcmtOperations = () => {
576
514
  }
577
515
  if (!file)
578
516
  return;
579
- setShowWaitPanel(true);
580
- setShowPrimary(inputDcmts.length > 1);
581
- setShowSecondary(true);
582
- setWaitPanelTitle(operationTitle);
583
517
  abortController = new AbortController();
518
+ // Come nel download: con un solo documento resta il solo avanzamento in byte.
519
+ const isBatch = inputDcmts.length > 1;
520
+ const panel = openWaitPanel({
521
+ title: operationTitle,
522
+ max: isBatch ? inputDcmts.length : 0,
523
+ showSecondary: isBatch,
524
+ showCounters: isBatch,
525
+ primaryLabel: isBatch ? SDKUI_Localizator.Documents : undefined,
526
+ secondaryLabel: isBatch ? 'File corrente' : undefined,
527
+ });
528
+ const publishBytes = (loaded, total) => {
529
+ const text = total > 0
530
+ ? `Uploading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}`
531
+ : '';
532
+ if (isBatch)
533
+ panel.setSecondary({ value: loaded, max: total, text });
534
+ else
535
+ panel.update({ value: loaded, max: total, subtitle: text });
536
+ };
584
537
  let result = [];
585
- setWaitPanelMaxValuePrimary(inputDcmts.length);
586
- let firstBlock = true;
587
- let maxFileSize = 0;
588
538
  for (let i = 0; i < inputDcmts.length; i++) {
539
+ await panel.waitWhilePaused();
589
540
  if (abortController.signal.aborted) {
590
541
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i + 1} documenti` });
542
+ panel.warning();
591
543
  break;
592
544
  }
593
545
  try {
594
- setWaitPanelTextPrimary(`${SDKUI_Localizator.FileUpload} ${i + 1} di ${inputDcmts.length}`);
546
+ if (isBatch)
547
+ panel.setSubtitle(`${SDKUI_Localizator.FileUpload} ${i + 1} di ${inputDcmts.length}`);
595
548
  const cacheKey = `${inputDcmts[i].TID}-${inputDcmts[i].DID}`;
596
549
  if (dcmtsFileCachePreview.has(cacheKey))
597
550
  removeDcmtsFileCache(cacheKey);
@@ -599,41 +552,30 @@ export const useDcmtOperations = () => {
599
552
  if (ue) {
600
553
  ue.TID = inputDcmts[i].TID;
601
554
  ue.DID = inputDcmts[i].DID;
602
- await ue.SubstituteFileAsync(file, operType, undefined, abortController.signal, (pd) => {
603
- if (firstBlock) {
604
- maxFileSize = pd.ProgressBarMaximum ?? 0;
605
- setWaitPanelMaxValueSecondary(maxFileSize);
606
- firstBlock = false;
607
- }
608
- setWaitPanelValueSecondary(pd.ProgressBarValue);
609
- setWaitPanelTextSecondary(`Uploading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`);
610
- if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
611
- setWaitPanelMaxValueSecondary(0);
612
- setWaitPanelValueSecondary(0);
613
- setWaitPanelTextSecondary('');
614
- firstBlock = true;
615
- }
616
- });
555
+ await ue.SubstituteFileAsync(file, operType, undefined, abortController.signal,
556
+ // Byte finiti ma il server elabora ancora: la barra si azzera invece di restare al 100%.
557
+ (pd) => pd.ProgressBarValue === pd.ProgressBarMaximum
558
+ ? publishBytes(0, 0)
559
+ : publishBytes(pd.ProgressBarValue, pd.ProgressBarMaximum ?? 0));
560
+ }
561
+ if (isBatch) {
562
+ publishBytes(0, 0);
563
+ panel.setValue(i + 1);
617
564
  }
618
- setWaitPanelValuePrimary(i + 1);
619
565
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.SUCCESS });
566
+ panel.ok();
620
567
  }
621
568
  catch (ex) {
622
569
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(ex) });
570
+ panel.error();
623
571
  const err = ex;
624
572
  if (err.name === 'CanceledError') {
625
573
  ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
626
574
  }
627
575
  }
628
576
  }
629
- setWaitPanelTextPrimary('');
630
- setWaitPanelMaxValuePrimary(0);
631
- setWaitPanelValuePrimary(0);
632
- setWaitPanelTextSecondary('');
633
- setWaitPanelMaxValueSecondary(0);
634
- setWaitPanelValueSecondary(0);
577
+ closeWaitPanel(panel);
635
578
  actionAfterOperationAsync?.();
636
- setShowWaitPanel(false);
637
579
  TMResultManager.show(result, operationTitle, "TID", "DID");
638
580
  };
639
581
  const getDcmtFileAsync = async (inputDcmt, rfo, operationTitle, keepWaitPanelPrimary, bypassCache = false) => {
@@ -644,29 +586,21 @@ export const useDcmtOperations = () => {
644
586
  const cacheKey = `${inputDcmt.TID}-${inputDcmt.DID}`;
645
587
  if (!bypassCache && dcmtsFileCachePreview.has(cacheKey))
646
588
  return { file: dcmtsFileCachePreview.get(cacheKey), isFromCache: true };
647
- setShowWaitPanel(true);
648
- setShowPrimary(keepWaitPanelPrimary);
649
- setShowSecondary(true);
650
- setWaitPanelTitle(operationTitle);
651
589
  abortController = new AbortController();
652
- let firstBlock = true;
653
- let maxFileSize = 0;
590
+ // Un file solo: l'unico avanzamento e' quello in byte e sta sulla barra principale.
591
+ const panel = openWaitPanel({
592
+ title: operationTitle,
593
+ subtitle: extensionHandler(inputDcmt.FILEEXT ?? '') === FileExtensionHandler.CONVERTIBLE ? 'Conversione file in corso...' : 'Recupero file in corso...',
594
+ showCounters: false,
595
+ });
654
596
  try {
655
- setWaitPanelTextSecondary(extensionHandler(inputDcmt.FILEEXT ?? '') === FileExtensionHandler.CONVERTIBLE ? 'Conversione file in corso...' : 'Recupero file in corso...');
656
597
  const file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(inputDcmt?.TID, inputDcmt?.DID, rfo, abortController.signal, (pd) => {
657
- if (firstBlock) {
658
- maxFileSize = pd.ProgressBarMaximum ?? 0;
659
- setWaitPanelMaxValueSecondary(maxFileSize);
660
- firstBlock = false;
661
- }
662
- setWaitPanelValueSecondary(pd.ProgressBarValue);
663
- setWaitPanelTextSecondary(`Downloading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`);
664
- if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
665
- setWaitPanelMaxValueSecondary(0);
666
- setWaitPanelValueSecondary(0);
667
- setWaitPanelTextSecondary('');
668
- firstBlock = true;
669
- }
598
+ const max = pd.ProgressBarMaximum ?? 0;
599
+ panel.update({
600
+ value: pd.ProgressBarValue,
601
+ max,
602
+ subtitle: `Downloading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(max, true)}`,
603
+ });
670
604
  });
671
605
  if (dcmtsFileCachePreview.size >= CACHE_SIZE_LIMIT) {
672
606
  const oldestKey = dcmtsFileCachePreview.keys().next().value;
@@ -678,10 +612,7 @@ export const useDcmtOperations = () => {
678
612
  return { file: file, isFromCache: false };
679
613
  }
680
614
  finally {
681
- setWaitPanelTextSecondary('');
682
- setWaitPanelMaxValueSecondary(0);
683
- setWaitPanelValueSecondary(0);
684
- setShowWaitPanel(false);
615
+ closeWaitPanel(panel);
685
616
  }
686
617
  };
687
618
  const getDisplayValueMetadataValueDescriptor = async (mvd) => {
@@ -738,19 +669,26 @@ export const useDcmtOperations = () => {
738
669
  return;
739
670
  }
740
671
  let zipEntries = [];
741
- setShowWaitPanel(true);
742
- setShowPrimary(true);
743
- setShowSecondary(false);
744
- setWaitPanelTitle(operationTitle);
745
672
  abortController = new AbortController();
673
+ // La copia conforme scarica due file per documento: e' l'unica operazione del ciclo
674
+ // con un avanzamento di dettaglio, e gli serve la seconda barra.
675
+ const isCopyCompliant = dcmtOperationType === DcmtOperationTypes.CopyCompliant;
676
+ const panel = openWaitPanel({
677
+ title: operationTitle,
678
+ max: inputDcmts.length,
679
+ showSecondary: isCopyCompliant,
680
+ primaryLabel: isCopyCompliant ? SDKUI_Localizator.Documents : undefined,
681
+ secondaryLabel: isCopyCompliant ? 'File corrente' : undefined,
682
+ });
746
683
  let result = [];
747
- setWaitPanelMaxValuePrimary(inputDcmts.length);
748
684
  for (let i = 0; i < inputDcmts.length; i++) {
685
+ await panel.waitWhilePaused();
749
686
  if (abortController.signal.aborted) {
750
687
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i + 1} documenti` });
688
+ panel.warning();
751
689
  break;
752
690
  }
753
- setWaitPanelTextPrimary(`Documento ${i + 1} di ${inputDcmts.length}`);
691
+ panel.setSubtitle(`Documento ${i + 1} di ${inputDcmts.length}`);
754
692
  try {
755
693
  switch (dcmtOperationType) {
756
694
  case DcmtOperationTypes.AddToFavs:
@@ -860,28 +798,29 @@ export const useDcmtOperations = () => {
860
798
  }
861
799
  }
862
800
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.SUCCESS });
801
+ panel.ok();
863
802
  }
864
803
  catch (ex) {
865
804
  let msg = getExceptionMessage(ex);
866
805
  if (msg.startsWith('WARNING#')) {
867
806
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: msg.replace('WARNING#', '') });
807
+ panel.warning();
868
808
  }
869
- else
809
+ else {
870
810
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.ERROR, description: msg });
811
+ panel.error();
812
+ }
871
813
  const err = ex;
872
814
  if (err.name === 'CanceledError') {
873
815
  ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
874
816
  }
875
817
  }
876
- setWaitPanelValuePrimary(i + 1);
818
+ panel.setValue(i + 1);
877
819
  }
878
820
  if (zipEntries.length > 0) {
879
821
  await zipDocuments(zipEntries, dcmtOperationType, inputDcmts[0].TID);
880
822
  }
881
- setWaitPanelMaxValuePrimary(0);
882
- setWaitPanelValuePrimary(0);
883
- setWaitPanelTextPrimary('');
884
- setShowWaitPanel(false);
823
+ closeWaitPanel(panel);
885
824
  actionAfterOperationAsync?.();
886
825
  TMResultManager.show(result, operationTitle, "TID", "DID");
887
826
  };
@@ -894,7 +833,6 @@ export const useDcmtOperations = () => {
894
833
  }
895
834
  };
896
835
  return {
897
- abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary,
898
836
  downloadDcmtsAsync, getDcmtFileAsync, clearDcmtsFileCache, removeDcmtsFileCache, isDcmtFileInCache, runOperationAsync, FileSourceDialog
899
837
  };
900
838
  };
@@ -63,6 +63,7 @@ export interface UIConfigProps {
63
63
  toppyHelpCenterUsePortal?: boolean;
64
64
  inputDcmtFormLayoutMode?: LayoutModes;
65
65
  graphometricManager?: IGraphometricManagerProp;
66
+ showSelector?: boolean;
66
67
  }
67
68
  export interface TasksProps {
68
69
  allTasks?: Array<TaskDescriptor>;