@topconsultnpm/sdkui-react 6.21.0-dev2.8 → 6.21.0-dev3.2

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 (81) hide show
  1. package/lib/components/base/TMAccordionNew.js +1 -0
  2. package/lib/components/base/TMAreaManager.js +19 -3
  3. package/lib/components/base/TMDataGrid.js +2 -2
  4. package/lib/components/base/TMModal.d.ts +1 -0
  5. package/lib/components/base/TMModal.js +2 -2
  6. package/lib/components/base/TMPanel.d.ts +7 -4
  7. package/lib/components/base/TMPanel.js +58 -26
  8. package/lib/components/base/TMTreeView.js +12 -2
  9. package/lib/components/base/TMWaitPanel.d.ts +3 -1
  10. package/lib/components/base/TMWaitPanel.js +14 -9
  11. package/lib/components/choosers/TMDistinctValues.js +35 -21
  12. package/lib/components/choosers/TMUserChooser.d.ts +4 -0
  13. package/lib/components/choosers/TMUserChooser.js +7 -5
  14. package/lib/components/editors/TMFormulaEditor.d.ts +2 -0
  15. package/lib/components/editors/TMFormulaEditor.js +75 -21
  16. package/lib/components/editors/TMMetadataValues.js +2 -1
  17. package/lib/components/editors/TMRadioButton.js +7 -5
  18. package/lib/components/editors/TMTextBox.d.ts +2 -0
  19. package/lib/components/editors/TMTextBox.js +3 -3
  20. package/lib/components/features/archive/TMArchive.js +1 -1
  21. package/lib/components/features/documents/TMCopyToFolderForm.d.ts +24 -0
  22. package/lib/components/features/documents/TMCopyToFolderForm.js +401 -0
  23. package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
  24. package/lib/components/features/documents/TMDcmtForm.js +126 -38
  25. package/lib/components/features/documents/TMDcmtFormActionButtons.js +17 -2
  26. package/lib/components/features/documents/TMDcmtPreview.d.ts +1 -0
  27. package/lib/components/features/documents/TMDcmtPreview.js +2 -2
  28. package/lib/components/features/documents/TMDcmtTasks.d.ts +1 -0
  29. package/lib/components/features/documents/TMDcmtTasks.js +2 -2
  30. package/lib/components/features/documents/TMDownloadRelationViewerSection.d.ts +23 -0
  31. package/lib/components/features/documents/TMDownloadRelationViewerSection.js +173 -0
  32. package/lib/components/features/documents/TMFileUploader.js +1 -1
  33. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
  34. package/lib/components/features/documents/TMMasterDetailDcmts.js +28 -9
  35. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +24 -0
  36. package/lib/components/features/documents/TMMergeToPdfForm.js +309 -0
  37. package/lib/components/features/documents/TMRelationViewer.d.ts +13 -0
  38. package/lib/components/features/documents/TMRelationViewer.js +75 -6
  39. package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +71 -0
  40. package/lib/components/features/documents/copyAndMergeDcmtsShared.js +304 -0
  41. package/lib/components/features/search/SignatureParamsManager.d.ts +70 -0
  42. package/lib/components/features/search/SignatureParamsManager.js +145 -0
  43. package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
  44. package/lib/components/features/search/TMSavedQuerySelector.js +3 -2
  45. package/lib/components/features/search/TMSearch.d.ts +4 -1
  46. package/lib/components/features/search/TMSearch.js +16 -10
  47. package/lib/components/features/search/TMSearchQueryPanel.js +1 -1
  48. package/lib/components/features/search/TMSearchResult.d.ts +2 -0
  49. package/lib/components/features/search/TMSearchResult.js +117 -22
  50. package/lib/components/features/workflow/diagram/queryDescriptorParser.js +3 -6
  51. package/lib/components/forms/Login/TMLoginForm.d.ts +9 -0
  52. package/lib/components/forms/Login/TMLoginForm.js +61 -0
  53. package/lib/components/forms/TMResultDialog.d.ts +1 -1
  54. package/lib/components/forms/TMResultDialog.js +4 -2
  55. package/lib/components/index.d.ts +1 -0
  56. package/lib/components/index.js +1 -0
  57. package/lib/components/pages/TMPage.js +3 -1
  58. package/lib/components/query/TMQueryEditor.js +1 -1
  59. package/lib/components/viewers/TMTidViewer.js +1 -1
  60. package/lib/helper/MergePdfManager.d.ts +45 -0
  61. package/lib/helper/MergePdfManager.js +145 -0
  62. package/lib/helper/SDKUI_Globals.d.ts +15 -0
  63. package/lib/helper/SDKUI_Globals.js +15 -1
  64. package/lib/helper/SDKUI_Localizator.d.ts +107 -2
  65. package/lib/helper/SDKUI_Localizator.js +1070 -12
  66. package/lib/helper/TMPdfViewer.js +25 -24
  67. package/lib/helper/TMUtils.d.ts +1 -0
  68. package/lib/helper/TMUtils.js +17 -0
  69. package/lib/helper/ZipManager.d.ts +56 -0
  70. package/lib/helper/ZipManager.js +127 -0
  71. package/lib/helper/index.d.ts +1 -0
  72. package/lib/helper/index.js +1 -0
  73. package/lib/hooks/useDataUserIdItem.js +6 -4
  74. package/lib/hooks/useDcmtOperations.d.ts +9 -2
  75. package/lib/hooks/useDcmtOperations.js +77 -34
  76. package/lib/hooks/useDocumentOperations.d.ts +3 -0
  77. package/lib/hooks/useDocumentOperations.js +233 -24
  78. package/lib/hooks/useForm.js +5 -2
  79. package/lib/hooks/useResizeObserver.d.ts +1 -1
  80. package/lib/hooks/useResizeObserver.js +16 -15
  81. package/package.json +4 -2
@@ -154,11 +154,11 @@ const TMPdfViewer = (props) => {
154
154
  // Pattern specifici per rilevare JavaScript effettivo nelle strutture PDF
155
155
  const jsPatterns = [
156
156
  // Esempio: /JavaScript [ (app.alert('Hello');) ]
157
- { name: 'JavaScript Dictionary Entry', pattern: /\/JavaScript\s*[(\[<][\s\S]*?[)\]>]/i },
157
+ { name: 'JavaScript Dictionary Entry', pattern: /\/JavaScript\s+[(\[<][\s\S]*?[)\]>]/i },
158
158
  // Esempio: /JS 15 0 R (riferimento a un oggetto JavaScript)
159
159
  { name: 'JavaScript Object Reference', pattern: /\/JS\s+\d+\s+\d+\s+R/i },
160
160
  // Esempio: /JS (app.alert('Click');) o /JS <hexstring>
161
- { name: 'Inline JavaScript Code', pattern: /\/JS\s*[(<][\s\S]*?[)>]/i },
161
+ { name: 'Inline JavaScript Code', pattern: /\/JS\s+[(<][\s\S]*?[)>]/i },
162
162
  // Esempio: /AA << /O << /S /JavaScript /JS (app.alert('Open');) >> >>
163
163
  { name: 'Additional Actions (AA) with JavaScript', pattern: /\/AA\s*<<[\s\S]*?\/JS[\s\S]*?>>/is },
164
164
  // Esempio: /OpenAction << /S /JavaScript /JS (this.print();) >>
@@ -258,11 +258,11 @@ const TMPdfViewer = (props) => {
258
258
  const normalizedIndex = normalizedContext.indexOf(normalizedMatch);
259
259
  if (normalizedIndex === -1) {
260
260
  // Se ancora non trova, mostra tutto in grassetto rosso
261
- return _jsx("strong", { style: { color: '#d32f2f', fontWeight: 'bold' }, children: context });
261
+ return _jsx("strong", { style: { color: '#e65100', fontWeight: 'bold' }, children: context });
262
262
  }
263
- return (_jsxs(_Fragment, { children: [normalizedContext.substring(0, normalizedIndex), _jsx("strong", { style: { color: '#d32f2f', fontWeight: 'bold', background: '#ffebee' }, children: normalizedContext.substring(normalizedIndex, normalizedIndex + normalizedMatch.length) }), normalizedContext.substring(normalizedIndex + normalizedMatch.length)] }));
263
+ return (_jsxs(_Fragment, { children: [normalizedContext.substring(0, normalizedIndex), _jsx("strong", { style: { color: '#e65100', fontWeight: 'bold', background: '#fff3e0' }, children: normalizedContext.substring(normalizedIndex, normalizedIndex + normalizedMatch.length) }), normalizedContext.substring(normalizedIndex + normalizedMatch.length)] }));
264
264
  }
265
- return (_jsxs(_Fragment, { children: [context.substring(0, matchIndex), _jsx("strong", { style: { color: '#d32f2f', fontWeight: 'bold', background: '#ffebee' }, children: context.substring(matchIndex, matchIndex + matchText.length) }), context.substring(matchIndex + matchText.length)] }));
265
+ return (_jsxs(_Fragment, { children: [context.substring(0, matchIndex), _jsx("strong", { style: { color: '#e65100', fontWeight: 'bold', background: '#fff3e0' }, children: context.substring(matchIndex, matchIndex + matchText.length) }), context.substring(matchIndex + matchText.length)] }));
266
266
  };
267
267
  TMMessageBoxManager.show({
268
268
  title: `${SDKUI_Localizator.Attention}: ${SDKUI_Localizator.PotentiallyUnsafeContent}`,
@@ -273,8 +273,8 @@ const TMPdfViewer = (props) => {
273
273
  message: (_jsxs("div", { style: { maxHeight: '500px', overflowY: 'auto', padding: '10px', lineHeight: '1.6' }, children: [_jsxs("div", { style: {
274
274
  marginBottom: '20px',
275
275
  padding: '12px',
276
- background: '#fff3cd',
277
- border: '1px solid #ffc107',
276
+ background: '#fff8e1',
277
+ border: '1px solid #ffe0b2',
278
278
  borderRadius: '6px',
279
279
  fontSize: '14px',
280
280
  wordBreak: 'normal',
@@ -282,22 +282,22 @@ const TMPdfViewer = (props) => {
282
282
  }, children: [_jsxs("strong", { children: [SDKUI_Localizator.Attention, ":"] }), " ", SDKUI_Localizator.PotentiallyUnsafeCodePatternsDetected.replaceParams(jsMatches.length.toString())] }), jsMatches.length > 0 ? (jsMatches.map((match, index) => (_jsxs("div", { style: {
283
283
  marginBottom: '16px',
284
284
  padding: '16px',
285
- border: '1px solid #ffcdd2',
285
+ border: '1px solid #ffe0b2',
286
286
  borderRadius: '8px',
287
287
  background: '#fff',
288
288
  boxShadow: '0 1px 3px rgba(0,0,0,0.1)'
289
289
  }, children: [_jsx("div", { style: {
290
290
  marginBottom: '12px',
291
291
  paddingBottom: '8px',
292
- borderBottom: '2px solid #f44336'
292
+ borderBottom: '2px solid #ffb74d'
293
293
  }, children: _jsxs("strong", { style: {
294
- color: '#d32f2f',
294
+ color: '#e65100',
295
295
  fontSize: '15px',
296
296
  display: 'flex',
297
297
  alignItems: 'center',
298
298
  gap: '8px'
299
299
  }, children: [_jsx("span", { style: {
300
- background: '#f44336',
300
+ background: '#ff9800',
301
301
  color: '#fff',
302
302
  borderRadius: '50%',
303
303
  width: '24px',
@@ -311,7 +311,7 @@ const TMPdfViewer = (props) => {
311
311
  background: '#f5f5f5',
312
312
  padding: '10px',
313
313
  borderRadius: '4px',
314
- borderLeft: '3px solid #f44336',
314
+ borderLeft: '3px solid #ff9800',
315
315
  fontFamily: 'Consolas, Monaco, monospace',
316
316
  fontSize: '12px',
317
317
  wordBreak: 'break-all',
@@ -408,23 +408,24 @@ const TMPdfViewer = (props) => {
408
408
  display: 'flex',
409
409
  justifyContent: 'center',
410
410
  alignItems: 'center',
411
- padding: '12px 20px',
412
- background: '#fff3cd',
413
- borderTop: '2px solid #ffc107',
414
- gap: '8px',
411
+ padding: '8px 16px',
412
+ background: '#fff8e1',
413
+ borderTop: '1px solid #ffe0b2',
414
+ gap: '5px',
415
415
  flexShrink: 0
416
- }, children: [_jsxs("span", { style: {
416
+ }, children: [jsMatches.length > 0 && (_jsx("span", { className: "dx-icon-info", style: {
417
+ fontSize: '18px',
418
+ color: '#e65100',
419
+ cursor: 'pointer',
420
+ transition: 'color 0.2s',
421
+ flexShrink: 0
422
+ }, onClick: showMatchDetails, title: "Clicca per vedere i dettagli", onMouseEnter: (e) => e.currentTarget.style.color = '#bf360c', onMouseLeave: (e) => e.currentTarget.style.color = '#e65100' })), _jsxs("span", { style: {
417
423
  color: '#856404',
424
+ fontSize: '13px',
418
425
  whiteSpace: 'nowrap',
419
426
  overflow: 'hidden',
420
427
  textOverflow: 'ellipsis',
421
428
  flex: 1
422
- }, children: [_jsx("strong", { children: "Attenzione:" }), " Questo documento contiene contenuti potenzialmente non sicuri."] }), jsMatches.length > 0 && (_jsx("span", { className: "dx-icon-info", style: {
423
- fontSize: '20px',
424
- color: '#d32f2f',
425
- cursor: 'pointer',
426
- transition: 'color 0.2s',
427
- marginLeft: '4px'
428
- }, onClick: showMatchDetails, title: "Clicca per vedere i dettagli", onMouseEnter: (e) => e.currentTarget.style.color = '#b71c1c', onMouseLeave: (e) => e.currentTarget.style.color = '#d32f2f' }))] }))] }) }));
429
+ }, children: [_jsx("strong", { children: "Attenzione:" }), " Questo documento contiene contenuti potenzialmente non sicuri."] })] }))] }) }));
429
430
  };
430
431
  export default TMPdfViewer;
@@ -53,4 +53,5 @@ type DcmtFormToolbarVisibility = {
53
53
  tmDcmtTasks: boolean;
54
54
  };
55
55
  export declare const getDcmtFormToolbarVisibility: (appModuleID: AppModules) => DcmtFormToolbarVisibility;
56
+ export declare const isConvertibleToPdfExt: (ext: string | undefined | null) => boolean;
56
57
  export {};
@@ -358,3 +358,20 @@ export const getDcmtFormToolbarVisibility = (appModuleID) => {
358
358
  };
359
359
  }
360
360
  };
361
+ // ---- Helper per file convertibili in PDF ----
362
+ export const isConvertibleToPdfExt = (ext) => {
363
+ if (!ext)
364
+ return false;
365
+ // Rimuove le estensioni di firma digitale (P7M, M7M, TSR, TSD, TS) prima di normalizzare
366
+ const normalized = ext.toString().trim().toUpperCase()
367
+ .replace(/\.P7M/g, '').replace(/\.M7M/g, '').replace(/\.TSR/g, '').replace(/\.TSD/g, '').replace(/\.TS/g, '')
368
+ .toLowerCase().replace(/^\./, '').split('.').pop() ?? '';
369
+ return [
370
+ 'doc', 'docx', 'dot', 'dotx', 'docm', 'dotm', 'odt', 'rtf', 'txt',
371
+ 'csv', 'xls', 'xlsx',
372
+ 'ppt', 'pptx', 'ppsx',
373
+ 'htm', 'html', 'xml',
374
+ 'bmp', 'jpg', 'jpeg', 'tif', 'tiff',
375
+ 'eml', 'msg'
376
+ ].includes(normalized);
377
+ };
@@ -0,0 +1,56 @@
1
+ /** File da aggiungere allo ZIP */
2
+ export interface ZipFileEntry {
3
+ /** Nome/percorso nello ZIP (es. "docs/file.txt") */
4
+ filename: string;
5
+ /** Contenuto: string, File, Blob, ArrayBuffer o Uint8Array */
6
+ data: File | Blob | string | ArrayBuffer | Uint8Array;
7
+ /** Commento opzionale */
8
+ comment?: string;
9
+ /** Data modifica (default: ora) */
10
+ lastModDate?: Date;
11
+ }
12
+ /** Opzioni creazione ZIP */
13
+ export interface ZipCreateOptions {
14
+ /** Password AES (opzionale) */
15
+ password?: string;
16
+ /** Compressione 0-9 (default: 6) */
17
+ compressionLevel?: number;
18
+ /** Cifratura: 1=128bit, 2=192bit, 3=256bit (default: 3) */
19
+ encryptionStrength?: 1 | 2 | 3;
20
+ /** Commento globale ZIP */
21
+ comment?: string;
22
+ /** Callback progresso: (indice, totale, nomeFile) */
23
+ onProgress?: (index: number, total: number, filename: string) => void;
24
+ /** Callback progresso singolo file: (bytesElaborati, bytesTotali) */
25
+ onEntryProgress?: (progress: number, total: number) => void;
26
+ /** AbortSignal per annullare */
27
+ signal?: AbortSignal;
28
+ }
29
+ /**
30
+ * Utility per creare file ZIP con password opzionale (AES-256).
31
+ * Tutti i metodi sono statici.
32
+ */
33
+ export declare class ZipManager {
34
+ /** Configura zip.js (useWebWorkers, maxWorkers) */
35
+ static configure(options: {
36
+ useWebWorkers?: boolean;
37
+ maxWorkers?: number;
38
+ }): Promise<void>;
39
+ /** Crea ZIP da array di file */
40
+ static createZip(files: ZipFileEntry[], options?: ZipCreateOptions): Promise<Blob>;
41
+ /** Crea ZIP da singolo file */
42
+ static createZipFromFile(filename: string, data: File | Blob | string | ArrayBuffer | Uint8Array, options?: ZipCreateOptions): Promise<Blob>;
43
+ /** Crea ZIP da oggetto { nomeFile: contenuto } */
44
+ static createZipFromMap(filesMap: Record<string, File | Blob | string | ArrayBuffer | Uint8Array>, options?: ZipCreateOptions): Promise<Blob>;
45
+ /** Crea ZIP e avvia download */
46
+ static createAndDownload(files: ZipFileEntry[], downloadFilename: string, options?: ZipCreateOptions): Promise<void>;
47
+ /** Scarica un Blob come file */
48
+ static downloadBlob(blob: Blob, filename: string): void;
49
+ /** Crea URL temporaneo per Blob (ricordarsi revokeObjectURL!) */
50
+ static createObjectURL(blob: Blob): string;
51
+ /** Rilascia URL creato con createObjectURL */
52
+ static revokeObjectURL(url: string): void;
53
+ /** Converte data nel reader appropriato per zip.js */
54
+ private static createReader;
55
+ }
56
+ export default ZipManager;
@@ -0,0 +1,127 @@
1
+ // ============================================================================
2
+ // LAZY LOADING
3
+ // ============================================================================
4
+ /** Flag per tracciare se la libreria è stata caricata */
5
+ let zipJsLoaded = false;
6
+ /** Carica @zip.js/zip.js on-demand */
7
+ const loadZipJs = async () => {
8
+ // if (!zipJsLoaded) {
9
+ // console.log('[ZipManager] 📦 Caricamento lazy di @zip.js/zip.js...');
10
+ // console.time('[ZipManager] Tempo caricamento');
11
+ // }
12
+ const module = await import("@zip.js/zip.js");
13
+ // if (!zipJsLoaded) {
14
+ // console.timeEnd('[ZipManager] Tempo caricamento');
15
+ // console.log('[ZipManager] ✅ Libreria caricata!');
16
+ // zipJsLoaded = true;
17
+ // }
18
+ zipJsLoaded = true;
19
+ return module;
20
+ };
21
+ // ============================================================================
22
+ // ZIP MANAGER CLASS
23
+ // ============================================================================
24
+ /**
25
+ * Utility per creare file ZIP con password opzionale (AES-256).
26
+ * Tutti i metodi sono statici.
27
+ */
28
+ export class ZipManager {
29
+ /** Configura zip.js (useWebWorkers, maxWorkers) */
30
+ static async configure(options) {
31
+ const { configure } = await loadZipJs();
32
+ configure(options);
33
+ }
34
+ // ========================================================================
35
+ // CREAZIONE ZIP
36
+ // ========================================================================
37
+ /** Crea ZIP da array di file */
38
+ static async createZip(files, options = {}) {
39
+ const { password, compressionLevel = 6, encryptionStrength = 3, comment, onProgress, onEntryProgress, signal } = options;
40
+ // Validazione password: stringa vuota = nessuna cifratura
41
+ const usePassword = typeof password === "string" && password.length > 0 ? password : undefined;
42
+ // Lazy load della libreria
43
+ const { BlobWriter, ZipWriter, BlobReader, TextReader, Uint8ArrayReader } = await loadZipJs();
44
+ const blobWriter = new BlobWriter("application/zip");
45
+ const zipWriter = new ZipWriter(blobWriter, { password: usePassword, zipCrypto: true });
46
+ try {
47
+ const total = files.length;
48
+ for (let i = 0; i < files.length; i++) {
49
+ if (signal?.aborted) {
50
+ throw new DOMException("Operazione annullata", "AbortError");
51
+ }
52
+ const file = files[i];
53
+ onProgress?.(i + 1, total, file.filename);
54
+ const reader = this.createReader(file.data, { BlobReader, TextReader, Uint8ArrayReader });
55
+ await zipWriter.add(file.filename, reader, {
56
+ comment: file.comment,
57
+ lastModDate: file.lastModDate,
58
+ signal,
59
+ password: usePassword,
60
+ encryptionStrength,
61
+ onprogress: onEntryProgress ? (progress, total) => onEntryProgress(progress, total) : undefined
62
+ });
63
+ }
64
+ const commentBytes = comment ? new TextEncoder().encode(comment) : undefined;
65
+ return await zipWriter.close(commentBytes);
66
+ }
67
+ catch (error) {
68
+ try {
69
+ await zipWriter.close();
70
+ }
71
+ catch { /* ignora */ }
72
+ throw error;
73
+ }
74
+ }
75
+ /** Crea ZIP da singolo file */
76
+ static async createZipFromFile(filename, data, options = {}) {
77
+ return this.createZip([{ filename, data }], options);
78
+ }
79
+ /** Crea ZIP da oggetto { nomeFile: contenuto } */
80
+ static async createZipFromMap(filesMap, options = {}) {
81
+ const files = Object.entries(filesMap).map(([filename, data]) => ({ filename, data }));
82
+ return this.createZip(files, options);
83
+ }
84
+ // ========================================================================
85
+ // DOWNLOAD
86
+ // ========================================================================
87
+ /** Crea ZIP e avvia download */
88
+ static async createAndDownload(files, downloadFilename, options = {}) {
89
+ const blob = await this.createZip(files, options);
90
+ this.downloadBlob(blob, downloadFilename);
91
+ }
92
+ /** Scarica un Blob come file */
93
+ static downloadBlob(blob, filename) {
94
+ const url = URL.createObjectURL(blob);
95
+ const link = document.createElement("a");
96
+ link.href = url;
97
+ link.download = filename;
98
+ link.style.display = "none";
99
+ document.body.appendChild(link);
100
+ link.click();
101
+ document.body.removeChild(link);
102
+ setTimeout(() => URL.revokeObjectURL(url), 100);
103
+ }
104
+ /** Crea URL temporaneo per Blob (ricordarsi revokeObjectURL!) */
105
+ static createObjectURL(blob) {
106
+ return URL.createObjectURL(blob);
107
+ }
108
+ /** Rilascia URL creato con createObjectURL */
109
+ static revokeObjectURL(url) {
110
+ URL.revokeObjectURL(url);
111
+ }
112
+ // ========================================================================
113
+ // PRIVATE
114
+ // ========================================================================
115
+ /** Converte data nel reader appropriato per zip.js */
116
+ static createReader(data, readers) {
117
+ const { BlobReader, TextReader, Uint8ArrayReader } = readers;
118
+ if (typeof data === "string")
119
+ return new TextReader(data);
120
+ if (data instanceof Uint8Array)
121
+ return new Uint8ArrayReader(data);
122
+ if (data instanceof ArrayBuffer)
123
+ return new Uint8ArrayReader(new Uint8Array(data));
124
+ return new BlobReader(data);
125
+ }
126
+ }
127
+ export default ZipManager;
@@ -15,3 +15,4 @@ export * from './GlobalStyles';
15
15
  export * from './checkinCheckoutManager';
16
16
  export * from './workItemsHelper';
17
17
  export * from './devextremeCustomMessages';
18
+ export * from './ZipManager';
@@ -15,3 +15,4 @@ export * from './GlobalStyles';
15
15
  export * from './checkinCheckoutManager';
16
16
  export * from './workItemsHelper';
17
17
  export * from './devextremeCustomMessages';
18
+ export * from './ZipManager';
@@ -15,7 +15,7 @@ export const useDataUserIdItem = () => {
15
15
  if (userIDs.size === 0)
16
16
  return;
17
17
  try {
18
- const results = await Promise.all(Array.from(userIDs).map(id => UserListCacheService.GetAsync(id).then(user => ({ id, user }))
18
+ const results = await Promise.all(Array.from(userIDs).filter(id => id > 0).map(id => UserListCacheService.GetAsync(id).then(user => ({ id, user }))
19
19
  .catch(() => ({ id, user: undefined }))));
20
20
  const newCache = new Map();
21
21
  results.forEach(({ id, user }) => {
@@ -70,17 +70,19 @@ export const useDataUserIdItem = () => {
70
70
  * @returns Elemento React per visualizzare l'utente
71
71
  */
72
72
  const renderUserIdViewer = useCallback((userId, showIcon = false, showTitile = true) => {
73
- const ud = userId && userId > 0 ? getUserItem(userId) : undefined;
73
+ const ud = userId !== undefined && userId > 0 ? getUserItem(userId) : undefined;
74
74
  const getIcon = () => {
75
75
  if (!showIcon)
76
76
  return null;
77
- if (!userId)
77
+ if (userId === undefined)
78
78
  return null;
79
79
  return ud ? _jsx(TMUserIcon, { ud: ud }) : _jsx("span", { title: showTitile ? SDKUI_Localizator.ValueNotPresent : undefined, style: { display: 'inline-flex', alignItems: 'center' }, children: _jsx(IconWarning, { color: TMColors.warning }) });
80
80
  };
81
81
  const getDescription = () => {
82
- if (!userId)
82
+ if (userId == null)
83
83
  return undefined;
84
+ if (userId === 0)
85
+ return SDKUI_Localizator.SystemUser;
84
86
  return ud ? getCompleteUserName(ud.domain, ud.name) : userId.toString() ?? SDKUI_Localizator.NoneSelection;
85
87
  };
86
88
  return (_jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: '4px', lineHeight: 1 }, children: [getIcon(), _jsx("span", { style: { lineHeight: 'normal' }, children: getDescription() })] }));
@@ -1,5 +1,12 @@
1
- import { RetrieveFileOptions, FileDescriptor } from '@topconsultnpm/sdk-ts';
1
+ import { RetrieveFileOptions, DcmtOpers, FileDescriptor, GeneralRetrieveFormats, InvoiceRetrieveFormats, OrderRetrieveFormats, FileFormats } from '@topconsultnpm/sdk-ts';
2
2
  import { DcmtInfo, DcmtOperationTypes, DownloadModes, DownloadTypes } from '../ts';
3
+ export interface RetrieveFormatOptions {
4
+ retrieveReason?: DcmtOpers;
5
+ cvtFormat?: FileFormats;
6
+ generalRetrieveFormat?: GeneralRetrieveFormats;
7
+ invoiceRetrieveFormat?: InvoiceRetrieveFormats;
8
+ orderRetrieveFormat?: OrderRetrieveFormats;
9
+ }
3
10
  export interface UseDcmtOperationsReturn {
4
11
  abortController: AbortController;
5
12
  showWaitPanel: boolean;
@@ -12,7 +19,7 @@ export interface UseDcmtOperationsReturn {
12
19
  waitPanelTextSecondary: string;
13
20
  waitPanelValueSecondary: number;
14
21
  waitPanelMaxValueSecondary: number;
15
- downloadDcmtsAsync: (inputDcmts: DcmtInfo[] | undefined, downloadType?: DownloadTypes, downloadMode?: DownloadModes, onFileDownloaded?: (dcmtFile: File) => void, confirmAttachments?: (list: FileDescriptor[]) => Promise<string[] | undefined>, skipConfirmation?: boolean) => Promise<void>;
22
+ downloadDcmtsAsync: (inputDcmts: DcmtInfo[] | undefined, downloadType?: DownloadTypes, downloadMode?: DownloadModes, onFileDownloaded?: (dcmtFile: File, dcmtInfo: DcmtInfo) => void | Promise<void>, confirmAttachments?: (list: FileDescriptor[]) => Promise<string[] | undefined>, skipConfirmation?: boolean, retrieveOptions?: RetrieveFormatOptions, useCache?: boolean, showSuccessAlert?: boolean) => Promise<void>;
16
23
  getDcmtFileAsync: (inputDcmt: DcmtInfo | undefined, rfo: RetrieveFileOptions, operationTitle: string, keepWaitPanelPrimary: boolean, bypassCache?: boolean) => Promise<{
17
24
  file: File | undefined;
18
25
  isFromCache: boolean;
@@ -18,16 +18,49 @@ const isScannerLicenseConfigured = () => {
18
18
  };
19
19
  let abortController = new AbortController();
20
20
  const downloadCountMap = new Map();
21
- const getDownloadFileName = (fileName) => {
22
- const firstDot = fileName.indexOf('.');
23
- const lastDot = fileName.lastIndexOf('.');
24
- if (firstDot === -1 || firstDot === lastDot)
25
- return fileName;
26
- const count = downloadCountMap.get(fileName) ?? 0;
27
- downloadCountMap.set(fileName, count + 1);
28
- if (count === 0)
29
- return fileName;
30
- return `${fileName.slice(0, firstDot)}(${count})${fileName.slice(firstDot)}`;
21
+ /**
22
+ * Genera il nome file per il download con fallback: backend → fileName → DID.
23
+ * Aggiunge estensione se mancante e gestisce duplicati per file multi-estensione.
24
+ */
25
+ const getDownloadFileName = (file, dcmtInfo) => {
26
+ // === FASE 1: Costruzione nome base con fallback ===
27
+ // Determina l'estensione con fallback
28
+ const fileExtFromBackend = file?.name?.split('.').pop()?.toLowerCase();
29
+ const fileExtFromDcmt = dcmtInfo.FILEEXT?.toLowerCase();
30
+ const fileExtension = fileExtFromBackend ?? fileExtFromDcmt ?? '';
31
+ let baseFileName;
32
+ // 1. Priorità massima: nome dal backend
33
+ if (file?.name) {
34
+ baseFileName = file.name;
35
+ }
36
+ // 2. Seconda priorità: nome salvato nel documento
37
+ else if (dcmtInfo.fileName) {
38
+ const hasExtension = dcmtInfo.fileName.includes('.');
39
+ baseFileName = hasExtension
40
+ ? dcmtInfo.fileName
41
+ : (fileExtension ? `${dcmtInfo.fileName}.${fileExtension}` : dcmtInfo.fileName);
42
+ }
43
+ // 3. Fallback finale: ID documento con estensione
44
+ else {
45
+ baseFileName = fileExtension ? `${dcmtInfo.DID}.${fileExtension}` : `${dcmtInfo.DID}`;
46
+ }
47
+ // === FASE 2: Gestione duplicati per file con estensioni multiple ===
48
+ const firstDot = baseFileName.indexOf('.');
49
+ const lastDot = baseFileName.lastIndexOf('.');
50
+ // Se non ci sono punti o c'è una sola estensione, ritorna il nome così com'è
51
+ if (firstDot === -1 || firstDot === lastDot) {
52
+ return baseFileName;
53
+ }
54
+ // Gestisce il contatore per evitare sovrascritture
55
+ const count = downloadCountMap.get(baseFileName) ?? 0;
56
+ downloadCountMap.set(baseFileName, count + 1);
57
+ // Prima occorrenza: nome originale
58
+ if (count === 0) {
59
+ return baseFileName;
60
+ }
61
+ // Download successivi: inserisce contatore dopo il nome base
62
+ // Es: "file.pdf.p7m" -> "file(1).pdf.p7m"
63
+ return `${baseFileName.slice(0, firstDot)}(${count})${baseFileName.slice(firstDot)}`;
31
64
  };
32
65
  export const useDcmtOperations = () => {
33
66
  const [showWaitPanel, setShowWaitPanel] = useState(false);
@@ -42,7 +75,7 @@ export const useDcmtOperations = () => {
42
75
  const [waitPanelMaxValueSecondary, setWaitPanelMaxValueSecondary] = useState(0);
43
76
  const { OpenFileDialog } = useFileDialog();
44
77
  const [selectFileSource, FileSourceDialog] = useFileSourceDialog();
45
- const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false) => {
78
+ const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true) => {
46
79
  if (inputDcmts === undefined)
47
80
  return;
48
81
  if (inputDcmts.length <= 0)
@@ -72,9 +105,13 @@ export const useDcmtOperations = () => {
72
105
  setWaitPanelTitle(operationTitle);
73
106
  abortController = new AbortController();
74
107
  const rfo = new RetrieveFileOptions();
75
- rfo.retrieveReason = DcmtOpers.ShowFile;
76
- rfo.invoiceRetrieveFormat = SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
77
- rfo.orderRetrieveFormat = SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
108
+ rfo.retrieveReason = retrieveOptions?.retrieveReason ?? DcmtOpers.ShowFile;
109
+ if (retrieveOptions?.cvtFormat !== undefined)
110
+ rfo.cvtFormat = retrieveOptions.cvtFormat;
111
+ if (retrieveOptions?.generalRetrieveFormat !== undefined)
112
+ rfo.generalRetrieveFormat = retrieveOptions.generalRetrieveFormat;
113
+ rfo.invoiceRetrieveFormat = retrieveOptions?.invoiceRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
114
+ rfo.orderRetrieveFormat = retrieveOptions?.orderRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
78
115
  let result = [];
79
116
  setWaitPanelMaxValuePrimary(inputDcmts.length);
80
117
  let firstBlock = true;
@@ -88,7 +125,7 @@ export const useDcmtOperations = () => {
88
125
  setWaitPanelTextPrimary(`Download file ${i + 1} di ${inputDcmts.length}`);
89
126
  let file;
90
127
  const cacheKey = `${inputDcmts[i].TID}-${inputDcmts[i].DID}`;
91
- if (dcmtsFileCacheDownload.has(cacheKey)) {
128
+ if (useCache && dcmtsFileCacheDownload.has(cacheKey)) {
92
129
  file = dcmtsFileCacheDownload.get(cacheKey);
93
130
  }
94
131
  else {
@@ -112,23 +149,29 @@ export const useDcmtOperations = () => {
112
149
  const fileURL = window.URL.createObjectURL(file);
113
150
  if (downloadMode === "openInNewWindow") {
114
151
  (onFileDownloaded && file)
115
- ? onFileDownloaded(file)
152
+ ? onFileDownloaded(file, inputDcmts[i])
116
153
  : window.open(fileURL, '_blank', 'noopener');
117
154
  }
118
155
  else {
119
- const alink2 = document.createElement('a');
120
- alink2.href = fileURL;
121
- const baseFileName = inputDcmts[i].fileName ?? (inputDcmts[i].FILEEXT ? `${inputDcmts[i].DID}.${inputDcmts[i].FILEEXT}` : file?.name);
122
- alink2.download = getDownloadFileName(baseFileName);
123
- alink2.target = "_blank";
124
- alink2.rel = "noreferrer";
125
- alink2.click();
156
+ if (onFileDownloaded && file) {
157
+ await onFileDownloaded(file, inputDcmts[i]);
158
+ }
159
+ else {
160
+ const alink2 = document.createElement('a');
161
+ alink2.href = fileURL;
162
+ alink2.download = getDownloadFileName(file, inputDcmts[i]);
163
+ alink2.target = "_blank";
164
+ alink2.rel = "noreferrer";
165
+ alink2.click();
166
+ }
126
167
  }
127
- if (dcmtsFileCacheDownload.size >= CACHE_SIZE_LIMIT) {
128
- const oldestKey = dcmtsFileCacheDownload.keys().next().value;
129
- dcmtsFileCacheDownload.delete(oldestKey);
168
+ if (useCache) {
169
+ if (dcmtsFileCacheDownload.size >= CACHE_SIZE_LIMIT) {
170
+ const oldestKey = dcmtsFileCacheDownload.keys().next().value;
171
+ dcmtsFileCacheDownload.delete(oldestKey);
172
+ }
173
+ dcmtsFileCacheDownload.set(cacheKey, file);
130
174
  }
131
- dcmtsFileCacheDownload.set(cacheKey, file);
132
175
  result.push({ rowIndex: i, id1: inputDcmts[i].TID, id2: inputDcmts[i].DID, resultType: ResultTypes.SUCCESS });
133
176
  }
134
177
  catch (ex) {
@@ -146,9 +189,9 @@ export const useDcmtOperations = () => {
146
189
  setWaitPanelMaxValueSecondary(0);
147
190
  setWaitPanelValueSecondary(0);
148
191
  setShowWaitPanel(false);
149
- TMResultManager.show(result, operationTitle, "TID", "DID");
192
+ TMResultManager.show(result, operationTitle, "TID", "DID", undefined, undefined, showSuccessAlert);
150
193
  };
151
- const _downloadAttachmentsAsync = async (inputDcmts, confirmAttachments) => {
194
+ const _downloadAttachmentsAsync = async (inputDcmts, confirmAttachments, showSuccessAlert = true) => {
152
195
  if (inputDcmts === undefined)
153
196
  return;
154
197
  if (inputDcmts.length !== 1)
@@ -196,7 +239,7 @@ export const useDcmtOperations = () => {
196
239
  }
197
240
  }
198
241
  }
199
- TMResultManager.show(result, operationTitle, "TID", "DID");
242
+ TMResultManager.show(result, operationTitle, "TID", "DID", undefined, undefined, showSuccessAlert);
200
243
  }
201
244
  catch (ex) {
202
245
  TMSpinner.hide();
@@ -208,11 +251,11 @@ export const useDcmtOperations = () => {
208
251
  TMExceptionBoxManager.show({ exception: err });
209
252
  }
210
253
  };
211
- const downloadDcmtsAsync = async (inputDcmts, downloadType = DownloadTypes.Attachment, downloadMode = "download", onFileDownloaded, confirmAttachments, skipConfirmation = false) => {
254
+ const downloadDcmtsAsync = async (inputDcmts, downloadType = DownloadTypes.Attachment, downloadMode = "download", onFileDownloaded, confirmAttachments, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true) => {
212
255
  switch (downloadType) {
213
- case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation);
214
- case DownloadTypes.Attachment: return await _downloadAttachmentsAsync(inputDcmts, confirmAttachments);
215
- default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation);
256
+ case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation, retrieveOptions, useCache, showSuccessAlert);
257
+ case DownloadTypes.Attachment: return await _downloadAttachmentsAsync(inputDcmts, confirmAttachments, showSuccessAlert);
258
+ default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert);
216
259
  }
217
260
  };
218
261
  const uploadDcmtsAsync = async (inputDcmts, operationTitle, operType, actionAfterOperationAsync) => {
@@ -38,6 +38,8 @@ export interface DocumentDataProps {
38
38
  s4TViewerDialogComponent?: React.ReactNode;
39
39
  };
40
40
  }
41
+ export type TMCopyToFolderMode = 'onlySelected' | 'customized';
42
+ export type TMCopyToFolderOperationType = 'copyToFolder' | 'mergeToPdf';
41
43
  export interface ExportDataProps {
42
44
  dataColumns?: Array<IColumnProps>;
43
45
  dataSource?: Array<any>;
@@ -110,6 +112,7 @@ export interface UseDocumentOperationsResult {
110
112
  isOpenBatchUpdate: boolean;
111
113
  isModifiedBatchUpdate: boolean;
112
114
  updateBatchUpdateForm: (value: boolean) => void;
115
+ closeDcmtFormHandler: () => void;
113
116
  handleSignApprove: () => void;
114
117
  showSearchTMDatagrid: boolean;
115
118
  showExportForm: boolean;