@topconsultnpm/sdkui-react 6.21.0-dev3.2 → 6.21.0-dev3.20
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/NewComponents/ContextMenu/styles.d.ts +4 -4
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +2 -2
- package/lib/components/base/TMDataGrid.js +12 -2
- package/lib/components/base/TMDataGridExportForm.js +19 -8
- package/lib/components/base/TMFileManagerDataGridView.js +4 -4
- package/lib/components/base/TMFileManagerThumbnailItems.js +3 -3
- package/lib/components/base/TMFileManagerUtils.d.ts +7 -0
- package/lib/components/base/TMFileManagerUtils.js +14 -1
- package/lib/components/base/TMModal.js +2 -2
- package/lib/components/base/TMTreeView.js +12 -15
- package/lib/components/choosers/TMDynDataListItemChooser.js +6 -1
- package/lib/components/editors/TMEditorStyled.d.ts +6 -6
- package/lib/components/editors/TMMetadataEditor.js +6 -2
- package/lib/components/editors/TMMetadataValues.js +10 -2
- package/lib/components/features/blog/TMBlogCommentForm.js +5 -2
- package/lib/components/features/documents/TMCopyToFolderForm.js +2 -2
- package/lib/components/features/documents/TMDcmtIcon.js +1 -1
- package/lib/components/features/documents/TMMergeToPdfForm.js +2 -2
- package/lib/components/features/documents/TMRelationViewer.js +6 -1
- package/lib/components/features/documents/copyAndMergeDcmtsShared.d.ts +0 -13
- package/lib/components/features/documents/copyAndMergeDcmtsShared.js +1 -39
- package/lib/components/features/search/TMMetadataOutputForm.d.ts +17 -0
- package/lib/components/features/search/TMMetadataOutputForm.js +225 -0
- package/lib/components/features/search/TMMetadataSorterForm.d.ts +17 -0
- package/lib/components/features/search/TMMetadataSorterForm.js +243 -0
- package/lib/components/features/search/TMSearchQueryEditor.js +14 -8
- package/lib/components/features/search/TMSearchQueryPanel.js +208 -58
- package/lib/components/features/search/TMSearchResult.js +2 -3
- package/lib/components/features/search/TMViewHistoryDcmt.js +1 -1
- package/lib/components/features/search/metadataFormHelper.d.ts +16 -0
- package/lib/components/features/search/metadataFormHelper.js +77 -0
- package/lib/components/grids/TMBlogAttachments.js +2 -2
- package/lib/components/grids/TMBlogsPost.js +5 -3
- package/lib/components/grids/TMBlogsPostUtils.d.ts +1 -0
- package/lib/components/grids/TMBlogsPostUtils.js +3 -1
- package/lib/helper/MergePdfManager.js +7 -4
- package/lib/helper/SDKUI_Globals.js +2 -1
- package/lib/helper/SDKUI_Localizator.d.ts +4 -0
- package/lib/helper/SDKUI_Localizator.js +40 -0
- package/lib/helper/TMUtils.d.ts +23 -0
- package/lib/helper/TMUtils.js +55 -0
- package/lib/helper/checkinCheckoutManager.d.ts +4 -3
- package/lib/helper/checkinCheckoutManager.js +29 -11
- package/lib/hooks/useCheckInOutOperations.d.ts +4 -3
- package/lib/hooks/useDataUserIdItem.js +1 -1
- package/lib/hooks/useDcmtOperations.d.ts +18 -1
- package/lib/hooks/useDcmtOperations.js +67 -21
- package/lib/hooks/useDocumentOperations.js +3 -3
- package/lib/hooks/useRelatedDocuments.js +4 -4
- package/lib/services/platform_services.d.ts +4 -4
- package/package.json +10 -7
|
@@ -176,7 +176,7 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
176
176
|
retrieveOptions.invoiceRetrieveFormat = settings.invoiceFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
|
|
177
177
|
retrieveOptions.orderRetrieveFormat = settings.orderFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
|
|
178
178
|
if (settings.exportMode === 'copy') {
|
|
179
|
-
await downloadDcmtsAsync(dcmtInfosToDownload, DownloadTypes.Dcmt, 'download', writeFileToFolder,
|
|
179
|
+
await downloadDcmtsAsync({ inputDcmts: dcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: writeFileToFolder, skipConfirmation: true, retrieveOptions, useCache: false });
|
|
180
180
|
}
|
|
181
181
|
else if (settings.exportMode === 'zip') {
|
|
182
182
|
const zipEntries = [];
|
|
@@ -199,7 +199,7 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
199
199
|
});
|
|
200
200
|
zipEntries.push({ filename: getUniqueZipFileName(targetFileName), data: file });
|
|
201
201
|
};
|
|
202
|
-
await downloadDcmtsAsync(dcmtInfosToDownload, DownloadTypes.Dcmt, 'download', collectFileForZip,
|
|
202
|
+
await downloadDcmtsAsync({ inputDcmts: dcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForZip, skipConfirmation: true, retrieveOptions, useCache: false });
|
|
203
203
|
if (zipEntries.length > 0) {
|
|
204
204
|
const zipFileName = settings.zipFileName.trim().toLowerCase().endsWith('.zip')
|
|
205
205
|
? settings.zipFileName.trim()
|
|
@@ -41,7 +41,7 @@ const TMDcmtIcon = ({ fileExtension, fileCount, isLexProt, isSigned, isMail, isS
|
|
|
41
41
|
await onDownloadDcmtsAsync?.(dcmt, DownloadTypes.Dcmt, effectiveDownloadMode);
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
44
|
-
await downloadDcmtsAsync(dcmt, DownloadTypes.Dcmt, effectiveDownloadMode);
|
|
44
|
+
await downloadDcmtsAsync({ inputDcmts: dcmt, downloadType: DownloadTypes.Dcmt, downloadMode: effectiveDownloadMode });
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}, [tid, did, fileExtension, downloadMode, openInOffice, onDownloadDcmtsAsync, downloadDcmtsAsync]);
|
|
@@ -24,7 +24,7 @@ import MergePdfManager from '../../../helper/MergePdfManager';
|
|
|
24
24
|
const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationViewer, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers }) => {
|
|
25
25
|
const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, } = useDcmtOperations();
|
|
26
26
|
const deviceType = useDeviceType();
|
|
27
|
-
const [pdfFileName, setPdfFileName] = useState(
|
|
27
|
+
const [pdfFileName, setPdfFileName] = useState(`${SDKUI_Localizator.Result.toLowerCase()}.pdf`);
|
|
28
28
|
const [destinationFolder, setDestinationFolder] = useState('Download');
|
|
29
29
|
const [selectedItemsRelationViewer, setSelectedItemsRelationViewer] = useState([]);
|
|
30
30
|
// ---- Stato per il merge PDF (progress bar) ----
|
|
@@ -143,7 +143,7 @@ const TMMergeToPdfForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationView
|
|
|
143
143
|
const collectFileForMerge = async (file, _dcmtInfo) => {
|
|
144
144
|
pdfFiles.push(file);
|
|
145
145
|
};
|
|
146
|
-
await downloadDcmtsAsync(pdfDcmtInfosToDownload, DownloadTypes.Dcmt, 'download', collectFileForMerge,
|
|
146
|
+
await downloadDcmtsAsync({ inputDcmts: pdfDcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForMerge, skipConfirmation: true, retrieveOptions: rfo, useCache: false });
|
|
147
147
|
if (pdfFiles.length === 0) {
|
|
148
148
|
TMMessageBoxManager.show({ message: SDKUI_Localizator.NoFilesAvailableForMerge, buttons: [ButtonNames.OK] });
|
|
149
149
|
return null;
|
|
@@ -1238,7 +1238,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1238
1238
|
cursor: 'pointer',
|
|
1239
1239
|
lineHeight: 1,
|
|
1240
1240
|
whiteSpace: 'nowrap'
|
|
1241
|
-
}, children: [allExpanded ? _jsx(IconChevronRight, { fontSize: 14 }) : _jsx(IconChevronDown, { fontSize: 14 }), _jsx("span", { children: allExpanded ? SDKUI_Localizator.CollapseAll : SDKUI_Localizator.ExpandAll })] }) })), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, itemsPerPage: 100, onSelectionChanged: handleSelectedItemsChanged, onItemContextMenu: onItemContextMenu
|
|
1241
|
+
}, children: [allExpanded ? _jsx(IconChevronRight, { fontSize: 14 }) : _jsx(IconChevronDown, { fontSize: 14 }), _jsx("span", { children: allExpanded ? SDKUI_Localizator.CollapseAll : SDKUI_Localizator.ExpandAll })] }) })), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, itemsPerPage: 100, onSelectionChanged: handleSelectedItemsChanged, onItemContextMenu: onItemContextMenu, shouldDelayFocusOnEvent: (node, e) => {
|
|
1242
|
+
// Ritarda il focus quando si clicca sull'icona del documento
|
|
1243
|
+
// per permettere al doppio click di funzionare
|
|
1244
|
+
const target = e.target;
|
|
1245
|
+
return !!target.closest('.tm-dcmt-icon');
|
|
1246
|
+
} }) }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? 'Caricamento documenti master' : 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: (abortController) => {
|
|
1242
1247
|
setTimeout(() => {
|
|
1243
1248
|
abortController?.abort();
|
|
1244
1249
|
}, 100);
|
|
@@ -19,19 +19,6 @@ export declare const isDirectoryPickerSupported: () => boolean;
|
|
|
19
19
|
* il file viene restituito senza firma.
|
|
20
20
|
*/
|
|
21
21
|
export declare const isPdfExt: (ext: string | null | undefined) => boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Estrae l'estensione completa da un nome file, gestendo estensioni composite
|
|
24
|
-
* a più livelli come .PDF.P7M, .XML.P7M.TS, etc.
|
|
25
|
-
*
|
|
26
|
-
* Esempi:
|
|
27
|
-
* - "documento.pdf" -> ".pdf"
|
|
28
|
-
* - "DCMT_123.PDF.P7M" -> ".PDF.P7M"
|
|
29
|
-
* - "fattura.xml.p7m" -> ".xml.p7m"
|
|
30
|
-
* - "example.XML.P7M.TS" -> ".XML.P7M.TS"
|
|
31
|
-
* - "file.tar.gz" -> ".gz" (tar.gz non è gestito come firma)
|
|
32
|
-
* - "file" -> ""
|
|
33
|
-
*/
|
|
34
|
-
export declare const getFullFileExtension: (fileName: string, depth?: number) => string;
|
|
35
22
|
/**
|
|
36
23
|
* Estrae il nome base del file (senza estensione completa).
|
|
37
24
|
* Gestisce estensioni composite come .PDF.P7M
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DcmtTypeListCacheService, LayoutModes, SDK_Globals } from '@topconsultnpm/sdk-ts';
|
|
2
|
-
import { searchResultToMetadataValues, DocumentDownloadSettings } from '../../../helper';
|
|
2
|
+
import { searchResultToMetadataValues, DocumentDownloadSettings, getFullFileExtension } from '../../../helper';
|
|
3
3
|
import { TMColors } from '../../../utils/theme';
|
|
4
4
|
/** Numero minimo di file PDF necessari per poterli unire in un unico documento. */
|
|
5
5
|
export const MIN_PDF_FOR_MERGE = 2;
|
|
@@ -61,44 +61,6 @@ export const isPdfExt = (ext) => {
|
|
|
61
61
|
const normalized = ext.trim().toLowerCase().replace(/^\./, '');
|
|
62
62
|
return normalized === 'pdf' || normalized === 'pdf.p7m' || normalized === 'pdf.tsd' || normalized === 'pdf.m7m';
|
|
63
63
|
};
|
|
64
|
-
/**
|
|
65
|
-
* Estensioni di firma/marca temporale note che possono avvolgere altre estensioni.
|
|
66
|
-
* Es: file.pdf.p7m, file.xml.p7m, file.docx.p7m, file.xml.p7m.ts
|
|
67
|
-
*/
|
|
68
|
-
const SIGNATURE_EXTENSIONS = new Set(['p7m', 'p7s', 'm7m', 'tsd', 'tsr', 'ts']);
|
|
69
|
-
/** Profondità massima di ricorsione per la ricerca di estensioni composite */
|
|
70
|
-
const MAX_EXTENSION_DEPTH = 5;
|
|
71
|
-
/**
|
|
72
|
-
* Estrae l'estensione completa da un nome file, gestendo estensioni composite
|
|
73
|
-
* a più livelli come .PDF.P7M, .XML.P7M.TS, etc.
|
|
74
|
-
*
|
|
75
|
-
* Esempi:
|
|
76
|
-
* - "documento.pdf" -> ".pdf"
|
|
77
|
-
* - "DCMT_123.PDF.P7M" -> ".PDF.P7M"
|
|
78
|
-
* - "fattura.xml.p7m" -> ".xml.p7m"
|
|
79
|
-
* - "example.XML.P7M.TS" -> ".XML.P7M.TS"
|
|
80
|
-
* - "file.tar.gz" -> ".gz" (tar.gz non è gestito come firma)
|
|
81
|
-
* - "file" -> ""
|
|
82
|
-
*/
|
|
83
|
-
export const getFullFileExtension = (fileName, depth = 0) => {
|
|
84
|
-
if (!fileName || depth > MAX_EXTENSION_DEPTH)
|
|
85
|
-
return '';
|
|
86
|
-
const lastDotIndex = fileName.lastIndexOf('.');
|
|
87
|
-
if (lastDotIndex <= 0)
|
|
88
|
-
return '';
|
|
89
|
-
const lastExtension = fileName.substring(lastDotIndex + 1).toLowerCase();
|
|
90
|
-
// Se l'ultima estensione è una firma/marca, cerca ricorsivamente le estensioni precedenti
|
|
91
|
-
if (SIGNATURE_EXTENSIONS.has(lastExtension)) {
|
|
92
|
-
const nameWithoutLastExt = fileName.substring(0, lastDotIndex);
|
|
93
|
-
const innerExtension = getFullFileExtension(nameWithoutLastExt, depth + 1);
|
|
94
|
-
if (innerExtension) {
|
|
95
|
-
// Concatena l'estensione interna con quella corrente
|
|
96
|
-
return innerExtension + fileName.substring(lastDotIndex);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
// Estensione singola (o estensione base dopo le firme)
|
|
100
|
-
return fileName.substring(lastDotIndex);
|
|
101
|
-
};
|
|
102
64
|
/**
|
|
103
65
|
* Estrae il nome base del file (senza estensione completa).
|
|
104
66
|
* Gestisce estensioni composite come .PDF.P7M
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MetadataDescriptor, QueryDescriptor, SelectItem } from "@topconsultnpm/sdk-ts";
|
|
2
|
+
interface TMMetadataOutputFormProps {
|
|
3
|
+
/** QueryDescriptor da cui estrarre i metadati disponibili */
|
|
4
|
+
qd?: QueryDescriptor;
|
|
5
|
+
/** Lista di SelectItem attualmente selezionati */
|
|
6
|
+
selectedSelectItems?: SelectItem[];
|
|
7
|
+
/** Consente di visualizzare i metadati di sistema (es. MID < 150) */
|
|
8
|
+
allowSysMetadata?: boolean;
|
|
9
|
+
/** Funzione predicato per filtrare i metadati (es. solo quelli con permesso canView) */
|
|
10
|
+
filterMetadata?: (value: MetadataDescriptor, index: number, array: MetadataDescriptor[]) => unknown;
|
|
11
|
+
/** Callback chiamata alla chiusura del form */
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
/** Callback chiamata quando l'utente conferma la selezione dei SelectItem */
|
|
14
|
+
onChoose?: (selectItems: SelectItem[] | undefined) => void;
|
|
15
|
+
}
|
|
16
|
+
declare const TMMetadataOutputForm: (props: TMMetadataOutputFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export default TMMetadataOutputForm;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { SDK_Localizator, SelectItem, SelectItemVisibilities } from "@topconsultnpm/sdk-ts";
|
|
4
|
+
import { IconAddCircleOutline, IconApply, IconClear, IconDelete, IconDraggabledots, IconUndo, SDKUI_Localizator } from "../../../helper";
|
|
5
|
+
import TMToppyMessage from "../../../helper/TMToppyMessage";
|
|
6
|
+
import { ButtonNames, TMMessageBoxManager } from "../../base/TMPopUp";
|
|
7
|
+
import { TMColors } from "../../../utils/theme";
|
|
8
|
+
import { StyledToolbarForm } from "../../base/Styled";
|
|
9
|
+
import TMButton from "../../base/TMButton";
|
|
10
|
+
import TMModal from "../../base/TMModal";
|
|
11
|
+
import { TMMetadataChooserForm } from "../../choosers/TMMetadataChooser";
|
|
12
|
+
import { TMMetadataIcon } from "../../viewers/TMMidViewer";
|
|
13
|
+
import { loadMetadataFromQd, removeDuplicatesByTidMid, areArraysEqual } from "./metadataFormHelper";
|
|
14
|
+
const TMMetadataOutputForm = (props) => {
|
|
15
|
+
const { qd, selectedSelectItems, allowSysMetadata = true, filterMetadata, onClose, onChoose } = props;
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// STATE
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Stato iniziale dei SelectItem (per confronto isModified)
|
|
20
|
+
const initialSelectItemsRef = useRef((selectedSelectItems ?? []).map(item => {
|
|
21
|
+
const newItem = new SelectItem();
|
|
22
|
+
newItem.tid = item.tid;
|
|
23
|
+
newItem.mid = item.mid;
|
|
24
|
+
newItem.visibility = item.visibility ?? SelectItemVisibilities.Visible;
|
|
25
|
+
return newItem;
|
|
26
|
+
}));
|
|
27
|
+
// Lista dei SelectItem correnti (inizializzata da selectedSelectItems)
|
|
28
|
+
const [selectItems, setSelectItems] = useState(() => {
|
|
29
|
+
// Clona i SelectItem iniziali per evitare mutazioni
|
|
30
|
+
return (selectedSelectItems ?? []).map(item => {
|
|
31
|
+
const newItem = new SelectItem();
|
|
32
|
+
newItem.tid = item.tid;
|
|
33
|
+
newItem.mid = item.mid;
|
|
34
|
+
newItem.visibility = item.visibility ?? SelectItemVisibilities.Visible;
|
|
35
|
+
return newItem;
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
// Calcola se ci sono modifiche rispetto allo stato iniziale
|
|
39
|
+
const isModified = useMemo(() => {
|
|
40
|
+
return !areArraysEqual(selectItems, initialSelectItemsRef.current);
|
|
41
|
+
}, [selectItems]);
|
|
42
|
+
// Lista "piatta" di tutti i metadati estratti dai DTD, con chiave univoca
|
|
43
|
+
const [metadataList, setMetadataList] = useState([]);
|
|
44
|
+
// Flag di caricamento
|
|
45
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
46
|
+
// Flag per mostrare il TMMetadataChooserForm
|
|
47
|
+
const [showMetadataChooser, setShowMetadataChooser] = useState(false);
|
|
48
|
+
// Item in fase di drag
|
|
49
|
+
const [draggingItem, setDraggingItem] = useState(undefined);
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// MEMOIZZAZIONI per evitare re-render del TMMetadataChooserForm
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Lista di TID_MID già selezionati (per escluderli dal chooser)
|
|
54
|
+
const selectedIDs = useMemo(() => selectItems.map(si => ({ tid: si.tid, mid: si.mid })), [selectItems]);
|
|
55
|
+
// Callback per chiudere il chooser
|
|
56
|
+
const handleCloseChooser = useCallback(() => {
|
|
57
|
+
setShowMetadataChooser(false);
|
|
58
|
+
}, []);
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// EFFECT: Carica i metadati al mount
|
|
61
|
+
// =============================================================================
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const load = async () => {
|
|
64
|
+
setIsLoading(true);
|
|
65
|
+
try {
|
|
66
|
+
const { metadata } = await loadMetadataFromQd(qd, filterMetadata);
|
|
67
|
+
setMetadataList(metadata);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error("Errore nel caricamento dei metadati:", error);
|
|
71
|
+
setMetadataList([]);
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
setIsLoading(false);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
load();
|
|
78
|
+
}, [qd, filterMetadata]);
|
|
79
|
+
// =============================================================================
|
|
80
|
+
// HELPER: Trova il MetadataDescriptor per un SelectItem
|
|
81
|
+
// =============================================================================
|
|
82
|
+
const getMetadataForSelectItem = useCallback((item) => {
|
|
83
|
+
return metadataList.find(md => md.customData1 === item.tid && md.id === item.mid);
|
|
84
|
+
}, [metadataList]);
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// HANDLER: Conferma la scelta dal chooser (sincronizza la lista completa)
|
|
87
|
+
// =============================================================================
|
|
88
|
+
const handleChooseFromChooser = useCallback((tid_mids) => {
|
|
89
|
+
// Se la lista è vuota o undefined, rimuovi tutti
|
|
90
|
+
if (!tid_mids || tid_mids.length === 0) {
|
|
91
|
+
setSelectItems([]);
|
|
92
|
+
setShowMetadataChooser(false);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
setSelectItems(prev => {
|
|
96
|
+
// Filtra quelli esistenti che sono ancora selezionati (mantenendo ordine)
|
|
97
|
+
const kept = prev.filter(si => tid_mids.some(tm => tm.tid === si.tid && tm.mid === si.mid));
|
|
98
|
+
// Trova i nuovi da aggiungere
|
|
99
|
+
const newItems = [];
|
|
100
|
+
for (const tm of tid_mids) {
|
|
101
|
+
const exists = prev.some(si => si.tid === tm.tid && si.mid === tm.mid);
|
|
102
|
+
if (!exists) {
|
|
103
|
+
const newItem = new SelectItem();
|
|
104
|
+
newItem.tid = tm.tid;
|
|
105
|
+
newItem.mid = tm.mid;
|
|
106
|
+
newItem.visibility = SelectItemVisibilities.Visible;
|
|
107
|
+
newItems.push(newItem);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return removeDuplicatesByTidMid([...kept, ...newItems]);
|
|
111
|
+
});
|
|
112
|
+
setShowMetadataChooser(false);
|
|
113
|
+
}, []);
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// HANDLER: Rimuove un metadato dall'output
|
|
116
|
+
// =============================================================================
|
|
117
|
+
const handleRemoveSelectItem = useCallback((index) => {
|
|
118
|
+
setSelectItems(prev => prev.filter((_, i) => i !== index));
|
|
119
|
+
}, []);
|
|
120
|
+
// =============================================================================
|
|
121
|
+
// DRAG & DROP HANDLERS
|
|
122
|
+
// =============================================================================
|
|
123
|
+
const handleDragStart = useCallback((e, item) => {
|
|
124
|
+
// Previeni il drag se il target è l'icona elimina
|
|
125
|
+
const target = e.target;
|
|
126
|
+
if (target.closest('[data-no-drag="true"]')) {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
setDraggingItem(item);
|
|
131
|
+
e.dataTransfer.setData('text/plain', '');
|
|
132
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
133
|
+
}, []);
|
|
134
|
+
const handleDragEnd = useCallback(() => {
|
|
135
|
+
setDraggingItem(undefined);
|
|
136
|
+
}, []);
|
|
137
|
+
const handleDragOver = useCallback((e) => {
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
e.dataTransfer.dropEffect = 'move';
|
|
140
|
+
}, []);
|
|
141
|
+
const handleDrop = useCallback((e, targetItem) => {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
if (!draggingItem)
|
|
144
|
+
return;
|
|
145
|
+
const currentIndex = selectItems.indexOf(draggingItem);
|
|
146
|
+
const targetIndex = selectItems.indexOf(targetItem);
|
|
147
|
+
if (currentIndex === -1 || targetIndex === -1 || currentIndex === targetIndex) {
|
|
148
|
+
setDraggingItem(undefined);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const listCopy = [...selectItems];
|
|
152
|
+
listCopy.splice(currentIndex, 1);
|
|
153
|
+
listCopy.splice(targetIndex, 0, draggingItem);
|
|
154
|
+
setSelectItems(listCopy);
|
|
155
|
+
setDraggingItem(undefined);
|
|
156
|
+
}, [draggingItem, selectItems]);
|
|
157
|
+
// =============================================================================
|
|
158
|
+
// HANDLER: Rollback allo stato iniziale
|
|
159
|
+
// =============================================================================
|
|
160
|
+
const handleRollback = useCallback(() => {
|
|
161
|
+
setSelectItems(initialSelectItemsRef.current.map(item => {
|
|
162
|
+
const newItem = new SelectItem();
|
|
163
|
+
newItem.tid = item.tid;
|
|
164
|
+
newItem.mid = item.mid;
|
|
165
|
+
newItem.visibility = item.visibility;
|
|
166
|
+
return newItem;
|
|
167
|
+
}));
|
|
168
|
+
}, []);
|
|
169
|
+
// =============================================================================
|
|
170
|
+
// HANDLER: Chiusura con conferma se modificato
|
|
171
|
+
// =============================================================================
|
|
172
|
+
const confirmCloseContainerId = "TMMetadataOutputFormConfirmClose";
|
|
173
|
+
const handleClose = useCallback(() => {
|
|
174
|
+
if (!isModified) {
|
|
175
|
+
onClose();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
TMMessageBoxManager.show({
|
|
179
|
+
parentId: confirmCloseContainerId,
|
|
180
|
+
message: SDKUI_Localizator.ConfirmOnCancel,
|
|
181
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
182
|
+
onButtonClick: (buttonClicked) => {
|
|
183
|
+
if (buttonClicked === ButtonNames.YES) {
|
|
184
|
+
onClose();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}, [isModified, onClose]);
|
|
189
|
+
// =============================================================================
|
|
190
|
+
// HANDLER: Conferma la selezione
|
|
191
|
+
// =============================================================================
|
|
192
|
+
const handleConfirm = useCallback(() => {
|
|
193
|
+
onChoose?.(selectItems.length > 0 ? selectItems : undefined);
|
|
194
|
+
onClose();
|
|
195
|
+
}, [selectItems, onChoose, onClose]);
|
|
196
|
+
// =============================================================================
|
|
197
|
+
// RENDER
|
|
198
|
+
// =============================================================================
|
|
199
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TMModal, { title: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QuerySelect}${selectItems.length > 0 ? ` (${selectItems.length})` : ''}`, width: "600px", height: "500px", onClose: handleClose, hidePopup: false, askClosingConfirm: isModified, children: [_jsx("div", { id: confirmCloseContainerId }), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', gap: '10px' }, children: [_jsxs(StyledToolbarForm, { children: [_jsx(TMButton, { caption: SDKUI_Localizator.Confirm, icon: _jsx(IconApply, {}), btnStyle: "toolbar", color: "success", disabled: !isModified, onClick: handleConfirm }), _jsx(TMButton, { caption: SDKUI_Localizator.Undo, icon: _jsx(IconUndo, {}), btnStyle: "toolbar", color: "tertiary", disabled: !isModified, onClick: handleRollback }), _jsx(TMButton, { caption: SDKUI_Localizator.Add, icon: _jsx(IconAddCircleOutline, {}), btnStyle: "toolbar", onClick: () => setShowMetadataChooser(true) }), _jsx(TMButton, { caption: SDKUI_Localizator.Clear, icon: _jsx(IconClear, {}), btnStyle: "toolbar", disabled: selectItems.length === 0, onClick: () => setSelectItems([]) })] }), _jsxs("div", { style: {
|
|
200
|
+
flex: 1,
|
|
201
|
+
overflow: 'auto',
|
|
202
|
+
border: `1px solid ${TMColors.border_normal}`,
|
|
203
|
+
borderRadius: '4px',
|
|
204
|
+
padding: '5px'
|
|
205
|
+
}, children: [isLoading && _jsxs("p", { style: { textAlign: 'center', padding: '20px' }, children: [SDKUI_Localizator.Loading, "..."] }), !isLoading && selectItems.length === 0 && (_jsx(TMToppyMessage, { message: SDKUI_Localizator.NoOutputMetadata, titleTooltip: SDKUI_Localizator.NoOutputMetadata })), !isLoading && selectItems.length > 0 && (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '2px' }, children: selectItems.map((item, index) => {
|
|
206
|
+
const md = getMetadataForSelectItem(item);
|
|
207
|
+
const isDragging = draggingItem === item;
|
|
208
|
+
return (_jsxs("div", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragEnd: handleDragEnd, onDragOver: handleDragOver, onDrop: (e) => handleDrop(e, item), style: {
|
|
209
|
+
display: 'flex',
|
|
210
|
+
alignItems: 'center',
|
|
211
|
+
gap: '6px',
|
|
212
|
+
padding: '4px 8px',
|
|
213
|
+
backgroundColor: isDragging ? TMColors.primary_container : TMColors.default_background,
|
|
214
|
+
borderRadius: '3px',
|
|
215
|
+
border: `1px solid ${isDragging ? TMColors.primaryColor : TMColors.border_normal}`,
|
|
216
|
+
minHeight: '32px',
|
|
217
|
+
cursor: 'grab',
|
|
218
|
+
transition: 'all 0.2s ease',
|
|
219
|
+
transform: isDragging ? 'scale(1.02)' : 'scale(1)',
|
|
220
|
+
boxShadow: isDragging ? '0 4px 12px rgba(0,0,0,0.15)' : 'none',
|
|
221
|
+
opacity: isDragging ? 0.9 : 1,
|
|
222
|
+
}, children: [_jsx("div", { style: { display: 'flex', cursor: 'grab' }, children: _jsx(IconDraggabledots, { fontSize: 15, color: TMColors.button_icon }) }), _jsx(TMMetadataIcon, { tid: item.tid ?? 0, md: md }), _jsx("span", { style: { flex: 1, fontSize: '13px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: md?.name ?? `MID: ${item.mid}` }), _jsx("div", { "data-no-drag": "true", onMouseDown: (e) => e.stopPropagation(), children: _jsx(TMButton, { caption: SDKUI_Localizator.Remove, icon: _jsx(IconDelete, { color: TMColors.error }), btnStyle: "icon", onClick: () => handleRemoveSelectItem(index) }) })] }, `${item.tid}_${item.mid}_${index}`));
|
|
223
|
+
}) }))] })] })] }), showMetadataChooser && (_jsx(TMMetadataChooserForm, { allowMultipleSelection: true, height: "600px", width: "700px", allowSysMetadata: allowSysMetadata, filterMetadata: filterMetadata, qd: qd, selectedIDs: selectedIDs, onClose: handleCloseChooser, onChoose: handleChooseFromChooser }))] }));
|
|
224
|
+
};
|
|
225
|
+
export default TMMetadataOutputForm;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MetadataDescriptor, OrderByItem, QueryDescriptor } from "@topconsultnpm/sdk-ts";
|
|
2
|
+
interface TMMetadataSorterFormProps {
|
|
3
|
+
/** QueryDescriptor da cui estrarre i metadati disponibili */
|
|
4
|
+
qd?: QueryDescriptor;
|
|
5
|
+
/** Lista di OrderByItem attualmente selezionati */
|
|
6
|
+
selectedOrderByItems?: OrderByItem[];
|
|
7
|
+
/** Consente di visualizzare i metadati di sistema (es. MID < 150) */
|
|
8
|
+
allowSysMetadata?: boolean;
|
|
9
|
+
/** Funzione predicato per filtrare i metadati (es. solo quelli con permesso canView) */
|
|
10
|
+
filterMetadata?: (value: MetadataDescriptor, index: number, array: MetadataDescriptor[]) => unknown;
|
|
11
|
+
/** Callback chiamata alla chiusura del form */
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
/** Callback chiamata quando l'utente conferma la selezione degli OrderByItem */
|
|
14
|
+
onChoose?: (orderByItems: OrderByItem[] | undefined) => void;
|
|
15
|
+
}
|
|
16
|
+
declare const TMMetadataSorterForm: (props: TMMetadataSorterFormProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export default TMMetadataSorterForm;
|