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

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 (97) 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/TMCounterContainer.d.ts +3 -1
  5. package/lib/components/base/TMCounterContainer.js +16 -1
  6. package/lib/components/base/TMCustomButton.js +40 -48
  7. package/lib/components/base/TMDataGrid.js +24 -4
  8. package/lib/components/base/TMPanel.js +173 -170
  9. package/lib/components/base/TMSpinner.d.ts +2 -1
  10. package/lib/components/base/TMSpinner.js +9 -7
  11. package/lib/components/base/TMTabControl.d.ts +5 -0
  12. package/lib/components/base/TMTabControl.js +18 -7
  13. package/lib/components/base/TMTreeView.js +20 -0
  14. package/lib/components/base/TMWaitPanel.js +1 -1
  15. package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
  16. package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
  17. package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +179 -0
  18. package/lib/components/base/waitPanel/TMWaitPanelManager.js +373 -0
  19. package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +58 -0
  20. package/lib/components/base/waitPanel/TMWaitPanelView.js +346 -0
  21. package/lib/components/base/waitPanel/index.d.ts +1 -0
  22. package/lib/components/base/waitPanel/index.js +1 -0
  23. package/lib/components/choosers/TMDistinctValues.js +3 -3
  24. package/lib/components/choosers/TMPathChooser.js +17 -34
  25. package/lib/components/editors/TMTextBox.d.ts +4 -0
  26. package/lib/components/editors/TMTextBox.js +99 -11
  27. package/lib/components/features/archive/TMArchive.js +7 -2
  28. package/lib/components/features/assistant/TMHelpOnLineChat.d.ts +28 -0
  29. package/lib/components/features/assistant/TMHelpOnLineChat.js +1231 -0
  30. package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
  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 +52 -56
  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 +51 -24
  54. package/lib/components/features/search/TMTreeSelector.js +23 -27
  55. package/lib/components/features/search/TMViewHistoryDcmt.js +15 -26
  56. package/lib/components/features/tasks/TMTasksView.js +97 -90
  57. package/lib/components/features/wg/TMWGsCopyMoveForm.js +104 -100
  58. package/lib/components/forms/TMResultDialog.js +1 -1
  59. package/lib/components/grids/TMBlogsPost.js +93 -97
  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 +32 -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 +7 -2
  77. package/lib/helper/SDKUI_Globals.js +7 -2
  78. package/lib/helper/SDKUI_Localizator.d.ts +31 -6
  79. package/lib/helper/SDKUI_Localizator.js +301 -51
  80. package/lib/helper/ZipManager.d.ts +1 -1
  81. package/lib/helper/ZipManager.js +2 -1
  82. package/lib/helper/checkinCheckoutManager.js +2 -3
  83. package/lib/helper/dcmtsHelper.d.ts +2 -0
  84. package/lib/helper/dcmtsHelper.js +3 -1
  85. package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
  86. package/lib/hooks/useCheckInOutOperations.js +16 -37
  87. package/lib/hooks/useDcmtOperations.d.ts +3 -19
  88. package/lib/hooks/useDcmtOperations.js +175 -229
  89. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  90. package/lib/hooks/useDocumentOperations.js +16 -29
  91. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
  92. package/lib/hooks/useMultiMasterDetailDcmts.js +70 -68
  93. package/lib/hooks/usePreventFileDrop.js +9 -0
  94. package/lib/hooks/useWorkflowApprove.js +33 -17
  95. package/lib/services/caseflow/CaseFlowService.d.ts +69 -9
  96. package/lib/services/caseflow/CaseFlowService.js +126 -58
  97. package/package.json +2 -2
@@ -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,24 @@ 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
+ /** Messaggio per il popover delle pill del pannello: il DID dice a quale documento si riferisce. */
125
+ const panelMessage = (dcmt, message) => `DID ${dcmt.DID}: ${message}`;
126
+ // Il pannello vive fuori dall'albero del chiamante: allo smontaggio va chiuso a mano.
127
+ useEffect(() => () => { waitPanelRef.current?.close(); waitPanelRef.current = null; }, []);
124
128
  const { OpenFileDialog } = useFileDialog();
125
129
  const [selectFileSource, FileSourceDialog] = useFileSourceDialog();
126
130
  const copyCompliantDcmtsAsync = async (inputDcmt, onFileDownloaded) => {
@@ -133,8 +137,14 @@ export const useDcmtOperations = () => {
133
137
  let returnDcmtInfos = [];
134
138
  const rfo = new RetrieveFileOptions();
135
139
  rfo.retrieveReason = DcmtOpers.ShowFile;
136
- let firstBlock = true;
137
- let maxFileSize = 0;
140
+ // Gira dentro runOperationAsync: i byte vanno sulla barra secondaria del pannello gia' aperto.
141
+ const publishSecondary = (pd, label) => {
142
+ const max = pd.ProgressBarMaximum ?? 0;
143
+ const completed = pd.ProgressBarValue === pd.ProgressBarMaximum;
144
+ waitPanelRef.current?.setSecondary(completed
145
+ ? { value: 0, max: 0, text: '' }
146
+ : { value: pd.ProgressBarValue, max, text: `${label} ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(max, true)}` });
147
+ };
138
148
  let signerInfoDescriptor = undefined;
139
149
  if (inputDcmt?.FILEEXT == undefined || inputDcmt.FILEEXT == '') {
140
150
  throw new Error('WARNING#' + SDKUI_Localizator.MetadataOnlyDocument);
@@ -183,21 +193,7 @@ export const useDcmtOperations = () => {
183
193
  else
184
194
  throw new Error('IdC non firmato e non marcato temporalmente!');
185
195
  //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
- });
196
+ 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
197
  let hashOk = false;
202
198
  if (fileIdC) {
203
199
  try {
@@ -214,21 +210,7 @@ export const useDcmtOperations = () => {
214
210
  throw new Error('Impronta non trovata nel file IdC o non corrispondente a quella del documento');
215
211
  }
216
212
  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
- });
213
+ file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(inputDcmt.TID, inputDcmt.DID, rfo, abortController.signal, (pd) => publishSecondary(pd, 'Downloading...'));
232
214
  if (file && fileIdC) {
233
215
  returnDcmtFiles.push(file);
234
216
  returnDcmtInfos.push(inputDcmt);
@@ -261,7 +243,7 @@ export const useDcmtOperations = () => {
261
243
  ShowAlert({ message: SDKUI_Localizator.ZipCreatedSavedInFolder.replaceParams(zipFileName, "Zip creato con successo"), mode: 'success', duration: 5000, title: SDKUI_Localizator.CompressToZipFile });
262
244
  }
263
245
  };
264
- const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false, downloadConcurrency = 1) => {
246
+ const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false) => {
265
247
  if (inputDcmts === undefined)
266
248
  return;
267
249
  if (inputDcmts.length <= 0)
@@ -285,86 +267,61 @@ export const useDcmtOperations = () => {
285
267
  }
286
268
  }
287
269
  let operationTitle = 'Download File';
288
- setShowWaitPanel(true);
289
- setShowPrimary(inputDcmts.length > 1);
290
- setShowSecondary(true);
291
- setWaitPanelTitle(operationTitle);
292
270
  abortController = new AbortController();
293
- // Riferimento const: il narrowing su inputDcmts non sopravvive dentro le closure del pool.
271
+ // Riferimento const: il narrowing su inputDcmts non sopravvive dentro le closure.
294
272
  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);
273
+ // Con un solo documento c'e' un avanzamento solo, quello dei byte, e sta sulla barra
274
+ // principale: la doppia barra (documenti + byte) serve solo all'elaborazione multipla.
275
+ const isBatch = dcmts.length > 1;
276
+ const panel = openWaitPanel({
277
+ title: operationTitle,
278
+ max: isBatch ? dcmts.length : 0,
279
+ showSecondary: isBatch,
280
+ showCounters: isBatch,
281
+ primaryLabel: isBatch ? SDKUI_Localizator.Documents : undefined,
282
+ secondaryLabel: isBatch ? 'File corrente' : undefined,
283
+ });
284
+ const rfo = new RetrieveFileOptions();
285
+ rfo.retrieveReason = retrieveOptions?.retrieveReason ?? DcmtOpers.ShowFile;
286
+ if (retrieveOptions?.cvtFormat !== undefined)
287
+ rfo.cvtFormat = retrieveOptions.cvtFormat;
288
+ if (retrieveOptions?.generalRetrieveFormat !== undefined)
289
+ rfo.generalRetrieveFormat = retrieveOptions.generalRetrieveFormat;
290
+ rfo.invoiceRetrieveFormat = retrieveOptions?.invoiceRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
291
+ rfo.orderRetrieveFormat = retrieveOptions?.orderRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
292
+ const results = [];
293
+ // Il pannello coalesce le scritture in un flush per frame: nessun throttle serve.
294
+ const publishBytes = (loaded, total) => {
295
+ const text = total > 0
296
+ ? `Downloading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}`
297
+ : '';
298
+ if (isBatch)
299
+ panel.setSecondary({ value: loaded, max: total, text });
300
+ else
301
+ panel.update({ value: loaded, max: total, subtitle: text });
339
302
  };
340
- // ---- Pool di download ----
341
- let nextIndex = 0;
342
- let started = 0;
303
+ // ---- Download sequenziale: un documento per volta ----
343
304
  let completed = 0;
344
- let abortReported = false;
345
305
  const downloadOne = async (i) => {
346
- started++;
347
- setWaitPanelTextPrimary(`Download file ${started} di ${dcmts.length}`);
306
+ if (isBatch)
307
+ panel.setSubtitle(`Download file ${i + 1} di ${dcmts.length}`);
348
308
  let file;
349
309
  const cacheKey = `${dcmts[i].TID}-${dcmts[i].DID}`;
350
310
  if (useCache && dcmtsFileCacheDownload.has(cacheKey)) {
351
311
  file = dcmtsFileCacheDownload.get(cacheKey);
352
312
  }
353
313
  else {
354
- inFlightBytes.set(i, { loaded: 0, total: 0 });
355
314
  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
- });
315
+ file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(dcmts[i].TID, dcmts[i].DID, rfo, abortController.signal, (pd) => publishBytes(pd.ProgressBarValue, pd.ProgressBarMaximum ?? 0));
360
316
  }
361
317
  finally {
362
- inFlightBytes.delete(i);
363
- publishSecondaryProgress();
318
+ if (isBatch)
319
+ publishBytes(0, 0);
364
320
  }
365
321
  }
366
322
  completed++;
367
- setWaitPanelValuePrimary(completed);
323
+ if (isBatch)
324
+ panel.setValue(completed);
368
325
  if (downloadMode === "openInNewWindow") {
369
326
  if (onFileDownloaded && file) {
370
327
  onFileDownloaded(file, dcmts[i]);
@@ -400,48 +357,34 @@ export const useDcmtOperations = () => {
400
357
  }
401
358
  dcmtsFileCacheDownload.set(cacheKey, file);
402
359
  }
403
- results[i] = { rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.SUCCESS };
360
+ results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.SUCCESS });
404
361
  };
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
- }
362
+ for (let i = 0; i < dcmts.length; i++) {
363
+ // Con la conferma di interruzione aperta il ciclo si ferma qui invece di
364
+ // continuare a scaricare dietro al dialogo.
365
+ await panel.waitWhilePaused();
366
+ if (abortController.signal.aborted) {
367
+ const description = SDKUI_Localizator.OperationDcmtsInterrupted.replaceParams(completed);
368
+ results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.WARNING, description });
369
+ panel.warning(1, description);
370
+ break;
371
+ }
372
+ try {
373
+ await downloadOne(i);
374
+ panel.ok();
375
+ }
376
+ catch (ex) {
377
+ const description = getExceptionMessage(ex);
378
+ results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.ERROR, description });
379
+ panel.error(1, panelMessage(dcmts[i], description));
380
+ const err = ex;
381
+ if (err.name === 'CanceledError') {
382
+ ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
427
383
  }
428
384
  }
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);
385
+ }
386
+ closeWaitPanel(panel);
387
+ TMResultManager.show(results, operationTitle, "TID", "DID", undefined, undefined, showSuccessAlert);
445
388
  };
446
389
  const _downloadAttachmentsAsync = async (inputDcmts, confirmAttachments, showSuccessAlert = true) => {
447
390
  if (inputDcmts === undefined)
@@ -482,7 +425,7 @@ export const useDcmtOperations = () => {
482
425
  // Download the selected files
483
426
  for (let i = 0; i < selectedFiles.length; i++) {
484
427
  if (abortController.signal.aborted) {
485
- result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i + 1} documenti` });
428
+ result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: SDKUI_Localizator.OperationItemsInterrupted.replaceParams(i) });
486
429
  break;
487
430
  }
488
431
  let fd = files.find(f => f.name === selectedFiles[i]);
@@ -505,13 +448,12 @@ export const useDcmtOperations = () => {
505
448
  };
506
449
  const downloadDcmtsAsync = async (params) => {
507
450
  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
451
  } = params;
510
452
  switch (downloadType) {
511
453
  // 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);
454
+ case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName);
513
455
  case DownloadTypes.Attachment: return await _downloadAttachmentsAsync(inputDcmts, confirmAttachments, showSuccessAlert);
514
- default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName, downloadConcurrency);
456
+ default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName);
515
457
  }
516
458
  };
517
459
  const uploadDcmtsAsync = async (inputDcmts, operationTitle, operType, actionAfterOperationAsync) => {
@@ -576,64 +518,69 @@ export const useDcmtOperations = () => {
576
518
  }
577
519
  if (!file)
578
520
  return;
579
- setShowWaitPanel(true);
580
- setShowPrimary(inputDcmts.length > 1);
581
- setShowSecondary(true);
582
- setWaitPanelTitle(operationTitle);
583
521
  abortController = new AbortController();
522
+ // Come nel download: con un solo documento resta il solo avanzamento in byte.
523
+ const isBatch = inputDcmts.length > 1;
524
+ const panel = openWaitPanel({
525
+ title: operationTitle,
526
+ max: isBatch ? inputDcmts.length : 0,
527
+ showSecondary: isBatch,
528
+ showCounters: isBatch,
529
+ primaryLabel: isBatch ? SDKUI_Localizator.Documents : undefined,
530
+ secondaryLabel: isBatch ? 'File corrente' : undefined,
531
+ });
532
+ const publishBytes = (loaded, total) => {
533
+ const text = total > 0
534
+ ? `Uploading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}`
535
+ : '';
536
+ if (isBatch)
537
+ panel.setSecondary({ value: loaded, max: total, text });
538
+ else
539
+ panel.update({ value: loaded, max: total, subtitle: text });
540
+ };
584
541
  let result = [];
585
- setWaitPanelMaxValuePrimary(inputDcmts.length);
586
- let firstBlock = true;
587
- let maxFileSize = 0;
588
542
  for (let i = 0; i < inputDcmts.length; i++) {
543
+ await panel.waitWhilePaused();
589
544
  if (abortController.signal.aborted) {
590
- result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i + 1} documenti` });
545
+ const description = SDKUI_Localizator.OperationDcmtsInterrupted.replaceParams(i);
546
+ result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description });
547
+ panel.warning(1, description);
591
548
  break;
592
549
  }
593
550
  try {
594
- setWaitPanelTextPrimary(`${SDKUI_Localizator.FileUpload} ${i + 1} di ${inputDcmts.length}`);
551
+ if (isBatch)
552
+ panel.setSubtitle(`${SDKUI_Localizator.FileUpload} ${i + 1} di ${inputDcmts.length}`);
595
553
  const cacheKey = `${inputDcmts[i].TID}-${inputDcmts[i].DID}`;
596
- if (dcmtsFileCachePreview.has(cacheKey))
597
- removeDcmtsFileCache(cacheKey);
554
+ removeDcmtsFileCache(cacheKey);
598
555
  let ue = SDK_Globals.tmSession?.NewUpdateEngineByID();
599
556
  if (ue) {
600
557
  ue.TID = inputDcmts[i].TID;
601
558
  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
- });
559
+ await ue.SubstituteFileAsync(file, operType, undefined, abortController.signal,
560
+ // Byte finiti ma il server elabora ancora: la barra si azzera invece di restare al 100%.
561
+ (pd) => pd.ProgressBarValue === pd.ProgressBarMaximum
562
+ ? publishBytes(0, 0)
563
+ : publishBytes(pd.ProgressBarValue, pd.ProgressBarMaximum ?? 0));
564
+ }
565
+ if (isBatch) {
566
+ publishBytes(0, 0);
567
+ panel.setValue(i + 1);
617
568
  }
618
- setWaitPanelValuePrimary(i + 1);
619
569
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.SUCCESS });
570
+ panel.ok();
620
571
  }
621
572
  catch (ex) {
622
- result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(ex) });
573
+ const description = getExceptionMessage(ex);
574
+ result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.ERROR, description });
575
+ panel.error(1, panelMessage(inputDcmts[i], description));
623
576
  const err = ex;
624
577
  if (err.name === 'CanceledError') {
625
578
  ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
626
579
  }
627
580
  }
628
581
  }
629
- setWaitPanelTextPrimary('');
630
- setWaitPanelMaxValuePrimary(0);
631
- setWaitPanelValuePrimary(0);
632
- setWaitPanelTextSecondary('');
633
- setWaitPanelMaxValueSecondary(0);
634
- setWaitPanelValueSecondary(0);
582
+ closeWaitPanel(panel);
635
583
  actionAfterOperationAsync?.();
636
- setShowWaitPanel(false);
637
584
  TMResultManager.show(result, operationTitle, "TID", "DID");
638
585
  };
639
586
  const getDcmtFileAsync = async (inputDcmt, rfo, operationTitle, keepWaitPanelPrimary, bypassCache = false) => {
@@ -644,29 +591,21 @@ export const useDcmtOperations = () => {
644
591
  const cacheKey = `${inputDcmt.TID}-${inputDcmt.DID}`;
645
592
  if (!bypassCache && dcmtsFileCachePreview.has(cacheKey))
646
593
  return { file: dcmtsFileCachePreview.get(cacheKey), isFromCache: true };
647
- setShowWaitPanel(true);
648
- setShowPrimary(keepWaitPanelPrimary);
649
- setShowSecondary(true);
650
- setWaitPanelTitle(operationTitle);
651
594
  abortController = new AbortController();
652
- let firstBlock = true;
653
- let maxFileSize = 0;
595
+ // Un file solo: l'unico avanzamento e' quello in byte e sta sulla barra principale.
596
+ const panel = openWaitPanel({
597
+ title: operationTitle,
598
+ subtitle: extensionHandler(inputDcmt.FILEEXT ?? '') === FileExtensionHandler.CONVERTIBLE ? 'Conversione file in corso...' : 'Recupero file in corso...',
599
+ showCounters: false,
600
+ });
654
601
  try {
655
- setWaitPanelTextSecondary(extensionHandler(inputDcmt.FILEEXT ?? '') === FileExtensionHandler.CONVERTIBLE ? 'Conversione file in corso...' : 'Recupero file in corso...');
656
602
  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
- }
603
+ const max = pd.ProgressBarMaximum ?? 0;
604
+ panel.update({
605
+ value: pd.ProgressBarValue,
606
+ max,
607
+ subtitle: `Downloading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(max, true)}`,
608
+ });
670
609
  });
671
610
  if (dcmtsFileCachePreview.size >= CACHE_SIZE_LIMIT) {
672
611
  const oldestKey = dcmtsFileCachePreview.keys().next().value;
@@ -678,10 +617,7 @@ export const useDcmtOperations = () => {
678
617
  return { file: file, isFromCache: false };
679
618
  }
680
619
  finally {
681
- setWaitPanelTextSecondary('');
682
- setWaitPanelMaxValueSecondary(0);
683
- setWaitPanelValueSecondary(0);
684
- setShowWaitPanel(false);
620
+ closeWaitPanel(panel);
685
621
  }
686
622
  };
687
623
  const getDisplayValueMetadataValueDescriptor = async (mvd) => {
@@ -738,19 +674,27 @@ export const useDcmtOperations = () => {
738
674
  return;
739
675
  }
740
676
  let zipEntries = [];
741
- setShowWaitPanel(true);
742
- setShowPrimary(true);
743
- setShowSecondary(false);
744
- setWaitPanelTitle(operationTitle);
745
677
  abortController = new AbortController();
678
+ // La copia conforme scarica due file per documento: e' l'unica operazione del ciclo
679
+ // con un avanzamento di dettaglio, e gli serve la seconda barra.
680
+ const isCopyCompliant = dcmtOperationType === DcmtOperationTypes.CopyCompliant;
681
+ const panel = openWaitPanel({
682
+ title: operationTitle,
683
+ max: inputDcmts.length,
684
+ showSecondary: isCopyCompliant,
685
+ primaryLabel: isCopyCompliant ? SDKUI_Localizator.Documents : undefined,
686
+ secondaryLabel: isCopyCompliant ? 'File corrente' : undefined,
687
+ });
746
688
  let result = [];
747
- setWaitPanelMaxValuePrimary(inputDcmts.length);
748
689
  for (let i = 0; i < inputDcmts.length; i++) {
690
+ await panel.waitWhilePaused();
749
691
  if (abortController.signal.aborted) {
750
- result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i + 1} documenti` });
692
+ const description = SDKUI_Localizator.OperationDcmtsInterrupted.replaceParams(i);
693
+ result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description });
694
+ panel.warning(1, description);
751
695
  break;
752
696
  }
753
- setWaitPanelTextPrimary(`Documento ${i + 1} di ${inputDcmts.length}`);
697
+ panel.setSubtitle(`Documento ${i + 1} di ${inputDcmts.length}`);
754
698
  try {
755
699
  switch (dcmtOperationType) {
756
700
  case DcmtOperationTypes.AddToFavs:
@@ -855,33 +799,36 @@ export const useDcmtOperations = () => {
855
799
  const importWarnings = await importCertificatesAsync(inputDcmts[i], i, fetchRemoteCertificates, abortController.signal);
856
800
  if (importWarnings) {
857
801
  result.push(...importWarnings);
802
+ importWarnings.forEach(w => panel.warning(1, panelMessage(inputDcmts[i], w.description ?? '')));
858
803
  }
859
804
  break;
860
805
  }
861
806
  }
862
807
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.SUCCESS });
808
+ panel.ok();
863
809
  }
864
810
  catch (ex) {
865
811
  let msg = getExceptionMessage(ex);
866
812
  if (msg.startsWith('WARNING#')) {
867
- result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description: msg.replace('WARNING#', '') });
813
+ const description = msg.replace('WARNING#', '');
814
+ result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.WARNING, description });
815
+ panel.warning(1, panelMessage(inputDcmts[i], description));
868
816
  }
869
- else
817
+ else {
870
818
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.ERROR, description: msg });
819
+ panel.error(1, panelMessage(inputDcmts[i], msg));
820
+ }
871
821
  const err = ex;
872
822
  if (err.name === 'CanceledError') {
873
823
  ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
874
824
  }
875
825
  }
876
- setWaitPanelValuePrimary(i + 1);
826
+ panel.setValue(i + 1);
877
827
  }
878
828
  if (zipEntries.length > 0) {
879
829
  await zipDocuments(zipEntries, dcmtOperationType, inputDcmts[0].TID);
880
830
  }
881
- setWaitPanelMaxValuePrimary(0);
882
- setWaitPanelValuePrimary(0);
883
- setWaitPanelTextPrimary('');
884
- setShowWaitPanel(false);
831
+ closeWaitPanel(panel);
885
832
  actionAfterOperationAsync?.();
886
833
  TMResultManager.show(result, operationTitle, "TID", "DID");
887
834
  };
@@ -894,7 +841,6 @@ export const useDcmtOperations = () => {
894
841
  }
895
842
  };
896
843
  return {
897
- abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary,
898
844
  downloadDcmtsAsync, getDcmtFileAsync, clearDcmtsFileCache, removeDcmtsFileCache, isDcmtFileInCache, runOperationAsync, FileSourceDialog
899
845
  };
900
846
  };
@@ -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>;