@topconsultnpm/sdkui-react 6.21.0-dev2.6 → 6.21.0-dev2.60
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.
- package/lib/components/base/TMAccordionNew.js +1 -0
- package/lib/components/base/TMAreaManager.js +19 -3
- package/lib/components/base/TMDataGrid.js +2 -2
- package/lib/components/base/TMModal.d.ts +1 -0
- package/lib/components/base/TMModal.js +2 -2
- package/lib/components/base/TMPanel.d.ts +7 -4
- package/lib/components/base/TMPanel.js +58 -26
- package/lib/components/base/TMTreeView.js +12 -2
- package/lib/components/base/TMWaitPanel.js +7 -4
- package/lib/components/choosers/TMDistinctValues.js +35 -21
- package/lib/components/choosers/TMUserChooser.d.ts +4 -0
- package/lib/components/choosers/TMUserChooser.js +7 -5
- package/lib/components/editors/TMFormulaEditor.d.ts +2 -0
- package/lib/components/editors/TMFormulaEditor.js +75 -21
- package/lib/components/editors/TMMetadataValues.js +2 -1
- package/lib/components/editors/TMRadioButton.js +7 -5
- package/lib/components/editors/TMTextBox.d.ts +2 -0
- package/lib/components/editors/TMTextBox.js +3 -3
- package/lib/components/features/archive/TMArchive.js +1 -1
- package/lib/components/features/documents/TMCopyToFolderForm.d.ts +24 -0
- package/lib/components/features/documents/TMCopyToFolderForm.js +379 -0
- package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
- package/lib/components/features/documents/TMDcmtForm.js +107 -29
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +17 -2
- package/lib/components/features/documents/TMDcmtPreview.d.ts +1 -0
- package/lib/components/features/documents/TMDcmtPreview.js +2 -2
- package/lib/components/features/documents/TMDcmtTasks.d.ts +1 -0
- package/lib/components/features/documents/TMDcmtTasks.js +2 -2
- package/lib/components/features/documents/TMDownloadRelationViewerSection.d.ts +23 -0
- package/lib/components/features/documents/TMDownloadRelationViewerSection.js +173 -0
- package/lib/components/features/documents/TMFileUploader.js +1 -1
- package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +4 -0
- package/lib/components/features/documents/TMMasterDetailDcmts.js +29 -9
- package/lib/components/features/documents/TMMergeToPdfForm.d.ts +26 -0
- package/lib/components/features/documents/TMMergeToPdfForm.js +293 -0
- package/lib/components/features/documents/TMRelationViewer.d.ts +13 -0
- package/lib/components/features/documents/TMRelationViewer.js +75 -6
- package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +71 -0
- package/lib/components/features/documents/copyAndMergeDcmtsShared.js +304 -0
- package/lib/components/features/search/SignatureParamsManager.d.ts +70 -0
- package/lib/components/features/search/SignatureParamsManager.js +145 -0
- package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
- package/lib/components/features/search/TMSavedQuerySelector.js +3 -2
- package/lib/components/features/search/TMSearch.d.ts +6 -1
- package/lib/components/features/search/TMSearch.js +16 -10
- package/lib/components/features/search/TMSearchQueryPanel.js +1 -1
- package/lib/components/features/search/TMSearchResult.d.ts +4 -0
- package/lib/components/features/search/TMSearchResult.js +118 -22
- package/lib/components/features/workflow/diagram/queryDescriptorParser.js +3 -6
- package/lib/components/forms/Login/TMLoginForm.d.ts +9 -0
- package/lib/components/forms/Login/TMLoginForm.js +61 -0
- package/lib/components/forms/TMResultDialog.d.ts +1 -1
- package/lib/components/forms/TMResultDialog.js +4 -2
- package/lib/components/grids/TMBlogAttachments.d.ts +1 -0
- package/lib/components/grids/TMBlogAttachments.js +38 -12
- package/lib/components/grids/TMBlogsPost.js +7 -1
- package/lib/components/grids/TMBlogsPostUtils.js +11 -17
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +1 -0
- package/lib/components/pages/TMPage.js +3 -1
- package/lib/components/query/TMQueryEditor.js +1 -1
- package/lib/components/viewers/TMTidViewer.js +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +15 -0
- package/lib/helper/SDKUI_Globals.js +15 -1
- package/lib/helper/SDKUI_Localizator.d.ts +106 -2
- package/lib/helper/SDKUI_Localizator.js +1060 -12
- package/lib/helper/TMPdfViewer.js +25 -24
- package/lib/helper/TMUtils.d.ts +20 -0
- package/lib/helper/TMUtils.js +17 -0
- package/lib/helper/ZipManager.d.ts +56 -0
- package/lib/helper/ZipManager.js +127 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/hooks/useDataUserIdItem.js +6 -4
- package/lib/hooks/useDcmtOperations.d.ts +9 -2
- package/lib/hooks/useDcmtOperations.js +77 -34
- package/lib/hooks/useDocumentOperations.d.ts +5 -0
- package/lib/hooks/useDocumentOperations.js +238 -27
- package/lib/hooks/useForm.js +5 -2
- package/lib/hooks/useResizeObserver.d.ts +1 -1
- package/lib/hooks/useResizeObserver.js +16 -15
- package/package.json +3 -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
|
|
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
|
|
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: '#
|
|
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: '#
|
|
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: '#
|
|
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: '#
|
|
277
|
-
border: '1px solid #
|
|
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 #
|
|
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 #
|
|
292
|
+
borderBottom: '2px solid #ffb74d'
|
|
293
293
|
}, children: _jsxs("strong", { style: {
|
|
294
|
-
color: '#
|
|
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: '#
|
|
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 #
|
|
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: '
|
|
412
|
-
background: '#
|
|
413
|
-
borderTop: '
|
|
414
|
-
gap: '
|
|
411
|
+
padding: '8px 16px',
|
|
412
|
+
background: '#fff8e1',
|
|
413
|
+
borderTop: '1px solid #ffe0b2',
|
|
414
|
+
gap: '5px',
|
|
415
415
|
flexShrink: 0
|
|
416
|
-
}, children: [
|
|
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."] })
|
|
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;
|
package/lib/helper/TMUtils.d.ts
CHANGED
|
@@ -53,4 +53,24 @@ type DcmtFormToolbarVisibility = {
|
|
|
53
53
|
tmDcmtTasks: boolean;
|
|
54
54
|
};
|
|
55
55
|
export declare const getDcmtFormToolbarVisibility: (appModuleID: AppModules) => DcmtFormToolbarVisibility;
|
|
56
|
+
export type MergePdfManagerType = {
|
|
57
|
+
merge: (files: File[], options?: {
|
|
58
|
+
onProgress?: (current: number, total: number, file: File) => void;
|
|
59
|
+
}) => Promise<{
|
|
60
|
+
bytes: Uint8Array;
|
|
61
|
+
blob: Blob;
|
|
62
|
+
pageCount: number;
|
|
63
|
+
}>;
|
|
64
|
+
mergeToFile: (files: File[], fileName?: string, options?: {
|
|
65
|
+
onProgress?: (current: number, total: number, file: File) => void;
|
|
66
|
+
}) => Promise<File>;
|
|
67
|
+
mergeAndDownload: (files: File[], fileName?: string, options?: {
|
|
68
|
+
onProgress?: (current: number, total: number, file: File) => void;
|
|
69
|
+
}) => Promise<{
|
|
70
|
+
bytes: Uint8Array;
|
|
71
|
+
blob: Blob;
|
|
72
|
+
pageCount: number;
|
|
73
|
+
}>;
|
|
74
|
+
};
|
|
75
|
+
export declare const isConvertibleToPdfExt: (ext: string | undefined | null) => boolean;
|
|
56
76
|
export {};
|
package/lib/helper/TMUtils.js
CHANGED
|
@@ -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;
|
package/lib/helper/index.d.ts
CHANGED
package/lib/helper/index.js
CHANGED
|
@@ -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 (
|
|
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 (
|
|
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
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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 (
|
|
128
|
-
|
|
129
|
-
|
|
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) => {
|
|
@@ -6,6 +6,7 @@ import { DcmtInfo, MetadataValueDescriptorEx, SearchResultContext, TaskContext }
|
|
|
6
6
|
import { UseCheckInOutOperationsReturn } from "./useCheckInOutOperations";
|
|
7
7
|
import { UseDcmtOperationsReturn } from "./useDcmtOperations";
|
|
8
8
|
import { UseRelatedDocumentsReturn } from "./useRelatedDocuments";
|
|
9
|
+
import { MergePdfManagerType } from '../helper';
|
|
9
10
|
export interface DocumentDataProps {
|
|
10
11
|
dtd: DcmtTypeDescriptor | undefined;
|
|
11
12
|
selectedItems: Array<any>;
|
|
@@ -38,6 +39,8 @@ export interface DocumentDataProps {
|
|
|
38
39
|
s4TViewerDialogComponent?: React.ReactNode;
|
|
39
40
|
};
|
|
40
41
|
}
|
|
42
|
+
export type TMCopyToFolderMode = 'onlySelected' | 'customized';
|
|
43
|
+
export type TMCopyToFolderOperationType = 'copyToFolder' | 'mergeToPdf';
|
|
41
44
|
export interface ExportDataProps {
|
|
42
45
|
dataColumns?: Array<IColumnProps>;
|
|
43
46
|
dataSource?: Array<any>;
|
|
@@ -97,6 +100,7 @@ interface UseDocumentOperationsProps {
|
|
|
97
100
|
tasks: TasksProps;
|
|
98
101
|
callbacks: OperationCallbacks;
|
|
99
102
|
exportData?: ExportDataProps;
|
|
103
|
+
mergePdfManager?: MergePdfManagerType;
|
|
100
104
|
}
|
|
101
105
|
export interface UseDocumentOperationsResult {
|
|
102
106
|
operationItems: Array<TMContextMenuItemProps>;
|
|
@@ -110,6 +114,7 @@ export interface UseDocumentOperationsResult {
|
|
|
110
114
|
isOpenBatchUpdate: boolean;
|
|
111
115
|
isModifiedBatchUpdate: boolean;
|
|
112
116
|
updateBatchUpdateForm: (value: boolean) => void;
|
|
117
|
+
closeDcmtFormHandler: () => void;
|
|
113
118
|
handleSignApprove: () => void;
|
|
114
119
|
showSearchTMDatagrid: boolean;
|
|
115
120
|
showExportForm: boolean;
|