@topconsultnpm/sdkui-react 6.21.0-dev3.9 → 6.21.0-dev4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +22 -1
- package/lib/components/NewComponents/FloatingMenuBar/styles.d.ts +6 -6
- package/lib/components/base/TMAreaManager.js +11 -0
- package/lib/components/base/TMDataGrid.js +12 -2
- package/lib/components/base/TMDataGridExportForm.js +19 -8
- package/lib/components/base/TMModal.js +2 -2
- package/lib/components/base/TMPopUp.d.ts +1 -0
- package/lib/components/base/TMPopUp.js +59 -2
- package/lib/components/base/TMSpinner.d.ts +4 -2
- package/lib/components/base/TMSpinner.js +33 -6
- package/lib/components/choosers/TMDistinctValues.js +1 -1
- package/lib/components/choosers/TMGroupChooser.js +1 -1
- package/lib/components/editors/TMEditorStyled.d.ts +4 -4
- package/lib/components/editors/TMFormulaEditor.d.ts +1 -0
- package/lib/components/editors/TMFormulaEditor.js +98 -49
- package/lib/components/editors/TMMetadataValues.js +23 -6
- package/lib/components/features/documents/TMMasterDetailDcmts.js +1 -1
- package/lib/components/features/documents/TMRelationViewer.js +3 -3
- 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/TMSearchQueryPanel.js +249 -58
- package/lib/components/features/search/TMSearchResult.d.ts +1 -0
- package/lib/components/features/search/TMSearchResult.js +39 -18
- package/lib/components/features/search/metadataFormHelper.d.ts +16 -0
- package/lib/components/features/search/metadataFormHelper.js +77 -0
- package/lib/components/forms/Login/TMLoginForm.js +15 -3
- package/lib/components/wizard/TMWizard.d.ts +1 -0
- package/lib/components/wizard/TMWizard.js +5 -3
- package/lib/helper/Enum_Localizator.js +1 -0
- package/lib/helper/SDKUI_Localizator.d.ts +4 -0
- package/lib/helper/SDKUI_Localizator.js +40 -0
- package/lib/helper/checkinCheckoutManager.js +10 -2
- package/lib/helper/helpers.d.ts +2 -1
- package/lib/helper/helpers.js +1 -0
- package/lib/hooks/useDataUserIdItem.js +1 -1
- package/lib/hooks/useDcmtOperations.js +168 -3
- package/lib/hooks/useDocumentOperations.js +19 -0
- package/lib/services/platform_services.d.ts +4 -4
- package/lib/ts/types.d.ts +2 -1
- package/lib/ts/types.js +1 -0
- package/package.json +62 -56
|
@@ -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;
|
|
@@ -0,0 +1,243 @@
|
|
|
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 { OrderByItem, SDK_Localizator } 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 TMDropDown from "../../editors/TMDropDown";
|
|
13
|
+
import { TMMetadataIcon } from "../../viewers/TMMidViewer";
|
|
14
|
+
import { loadMetadataFromQd, removeDuplicatesByTidMid, areArraysEqual } from "./metadataFormHelper";
|
|
15
|
+
const TMMetadataSorterForm = (props) => {
|
|
16
|
+
const { qd, selectedOrderByItems, allowSysMetadata = true, filterMetadata, onClose, onChoose } = props;
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// STATE
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Stato iniziale degli OrderByItem (per confronto isModified)
|
|
21
|
+
const initialOrderByItemsRef = useRef((selectedOrderByItems ?? []).map(item => {
|
|
22
|
+
const newItem = new OrderByItem();
|
|
23
|
+
newItem.tid = item.tid;
|
|
24
|
+
newItem.mid = item.mid;
|
|
25
|
+
newItem.asc = item.asc ?? true;
|
|
26
|
+
return newItem;
|
|
27
|
+
}));
|
|
28
|
+
// Lista degli OrderByItem correnti (inizializzata da selectedOrderByItems)
|
|
29
|
+
const [orderByItems, setOrderByItems] = useState(() => {
|
|
30
|
+
// Clona gli OrderByItem iniziali per evitare mutazioni
|
|
31
|
+
return (selectedOrderByItems ?? []).map(item => {
|
|
32
|
+
const newItem = new OrderByItem();
|
|
33
|
+
newItem.tid = item.tid;
|
|
34
|
+
newItem.mid = item.mid;
|
|
35
|
+
newItem.asc = item.asc ?? true;
|
|
36
|
+
return newItem;
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
// Calcola se ci sono modifiche rispetto allo stato iniziale
|
|
40
|
+
const isModified = useMemo(() => {
|
|
41
|
+
return !areArraysEqual(orderByItems, initialOrderByItemsRef.current, (a, b) => a.asc === b.asc);
|
|
42
|
+
}, [orderByItems]);
|
|
43
|
+
// Lista "piatta" di tutti i metadati estratti dai DTD, con chiave univoca
|
|
44
|
+
const [metadataList, setMetadataList] = useState([]);
|
|
45
|
+
// Flag di caricamento
|
|
46
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
47
|
+
// Flag per mostrare il TMMetadataChooserForm
|
|
48
|
+
const [showMetadataChooser, setShowMetadataChooser] = useState(false);
|
|
49
|
+
// Item in fase di drag
|
|
50
|
+
const [draggingItem, setDraggingItem] = useState(undefined);
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// MEMOIZZAZIONI per evitare re-render del TMMetadataChooserForm
|
|
53
|
+
// =============================================================================
|
|
54
|
+
// Lista di TID_MID già selezionati (per escluderli dal chooser)
|
|
55
|
+
const selectedIDs = useMemo(() => orderByItems.map(oi => ({ tid: oi.tid, mid: oi.mid })), [orderByItems]);
|
|
56
|
+
// Callback per chiudere il chooser
|
|
57
|
+
const handleCloseChooser = useCallback(() => {
|
|
58
|
+
setShowMetadataChooser(false);
|
|
59
|
+
}, []);
|
|
60
|
+
// =============================================================================
|
|
61
|
+
// EFFECT: Carica i metadati al mount
|
|
62
|
+
// =============================================================================
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
const load = async () => {
|
|
65
|
+
setIsLoading(true);
|
|
66
|
+
try {
|
|
67
|
+
const { metadata } = await loadMetadataFromQd(qd, filterMetadata);
|
|
68
|
+
setMetadataList(metadata);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error("Errore nel caricamento dei metadati:", error);
|
|
72
|
+
setMetadataList([]);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
load();
|
|
79
|
+
}, [qd, filterMetadata]);
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// HELPER: Trova il MetadataDescriptor per un OrderByItem
|
|
82
|
+
// =============================================================================
|
|
83
|
+
const getMetadataForOrderByItem = useCallback((item) => {
|
|
84
|
+
return metadataList.find(md => md.customData1 === item.tid && md.id === item.mid);
|
|
85
|
+
}, [metadataList]);
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// HANDLER: Conferma la scelta dal chooser (sincronizza la lista completa)
|
|
88
|
+
// =============================================================================
|
|
89
|
+
const handleChooseFromChooser = useCallback((tid_mids) => {
|
|
90
|
+
// Se la lista è vuota o undefined, rimuovi tutti
|
|
91
|
+
if (!tid_mids || tid_mids.length === 0) {
|
|
92
|
+
setOrderByItems([]);
|
|
93
|
+
setShowMetadataChooser(false);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
setOrderByItems(prev => {
|
|
97
|
+
// Filtra quelli esistenti che sono ancora selezionati (mantenendo ordine e impostazioni asc/desc)
|
|
98
|
+
const kept = prev.filter(oi => tid_mids.some(tm => tm.tid === oi.tid && tm.mid === oi.mid));
|
|
99
|
+
// Trova i nuovi da aggiungere
|
|
100
|
+
const newItems = [];
|
|
101
|
+
for (const tm of tid_mids) {
|
|
102
|
+
const exists = prev.some(oi => oi.tid === tm.tid && oi.mid === tm.mid);
|
|
103
|
+
if (!exists) {
|
|
104
|
+
const newItem = new OrderByItem();
|
|
105
|
+
newItem.tid = tm.tid;
|
|
106
|
+
newItem.mid = tm.mid;
|
|
107
|
+
newItem.asc = true;
|
|
108
|
+
newItems.push(newItem);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return removeDuplicatesByTidMid([...kept, ...newItems]);
|
|
112
|
+
});
|
|
113
|
+
setShowMetadataChooser(false);
|
|
114
|
+
}, []);
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// HANDLER: Rimuove un ordinamento
|
|
117
|
+
// =============================================================================
|
|
118
|
+
const handleRemoveOrderBy = useCallback((index) => {
|
|
119
|
+
setOrderByItems(prev => prev.filter((_, i) => i !== index));
|
|
120
|
+
}, []);
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// HANDLER: Toggle ascendente/discendente
|
|
123
|
+
// =============================================================================
|
|
124
|
+
const handleChangeAsc = useCallback((index, asc) => {
|
|
125
|
+
setOrderByItems(prev => prev.map((item, i) => {
|
|
126
|
+
if (i !== index)
|
|
127
|
+
return item;
|
|
128
|
+
const newItem = new OrderByItem();
|
|
129
|
+
newItem.tid = item.tid;
|
|
130
|
+
newItem.mid = item.mid;
|
|
131
|
+
newItem.asc = asc;
|
|
132
|
+
return newItem;
|
|
133
|
+
}));
|
|
134
|
+
}, []);
|
|
135
|
+
// =============================================================================
|
|
136
|
+
// DRAG & DROP HANDLERS
|
|
137
|
+
// =============================================================================
|
|
138
|
+
const handleDragStart = useCallback((e, item) => {
|
|
139
|
+
// Previeni il drag se il target è l'icona elimina o il dropdown
|
|
140
|
+
const target = e.target;
|
|
141
|
+
if (target.closest('[data-no-drag="true"]')) {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
setDraggingItem(item);
|
|
146
|
+
e.dataTransfer.setData('text/plain', '');
|
|
147
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
148
|
+
}, []);
|
|
149
|
+
const handleDragEnd = useCallback(() => {
|
|
150
|
+
setDraggingItem(undefined);
|
|
151
|
+
}, []);
|
|
152
|
+
const handleDragOver = useCallback((e) => {
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
e.dataTransfer.dropEffect = 'move';
|
|
155
|
+
}, []);
|
|
156
|
+
const handleDrop = useCallback((e, targetItem) => {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
if (!draggingItem)
|
|
159
|
+
return;
|
|
160
|
+
const currentIndex = orderByItems.indexOf(draggingItem);
|
|
161
|
+
const targetIndex = orderByItems.indexOf(targetItem);
|
|
162
|
+
if (currentIndex === -1 || targetIndex === -1 || currentIndex === targetIndex) {
|
|
163
|
+
setDraggingItem(undefined);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const listCopy = [...orderByItems];
|
|
167
|
+
listCopy.splice(currentIndex, 1);
|
|
168
|
+
listCopy.splice(targetIndex, 0, draggingItem);
|
|
169
|
+
setOrderByItems(listCopy);
|
|
170
|
+
setDraggingItem(undefined);
|
|
171
|
+
}, [draggingItem, orderByItems]);
|
|
172
|
+
// =============================================================================
|
|
173
|
+
// HANDLER: Rollback allo stato iniziale
|
|
174
|
+
// =============================================================================
|
|
175
|
+
const handleRollback = useCallback(() => {
|
|
176
|
+
setOrderByItems(initialOrderByItemsRef.current.map(item => {
|
|
177
|
+
const newItem = new OrderByItem();
|
|
178
|
+
newItem.tid = item.tid;
|
|
179
|
+
newItem.mid = item.mid;
|
|
180
|
+
newItem.asc = item.asc;
|
|
181
|
+
return newItem;
|
|
182
|
+
}));
|
|
183
|
+
}, []);
|
|
184
|
+
// =============================================================================
|
|
185
|
+
// HANDLER: Chiusura con conferma se modificato
|
|
186
|
+
// =============================================================================
|
|
187
|
+
const confirmCloseContainerId = "TMMetadataSorterFormConfirmClose";
|
|
188
|
+
const handleClose = useCallback(() => {
|
|
189
|
+
if (!isModified) {
|
|
190
|
+
onClose();
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
TMMessageBoxManager.show({
|
|
194
|
+
parentId: confirmCloseContainerId,
|
|
195
|
+
message: SDKUI_Localizator.ConfirmOnCancel,
|
|
196
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
197
|
+
onButtonClick: (buttonClicked) => {
|
|
198
|
+
if (buttonClicked === ButtonNames.YES) {
|
|
199
|
+
onClose();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}, [isModified, onClose]);
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// HANDLER: Conferma la selezione
|
|
206
|
+
// =============================================================================
|
|
207
|
+
const handleConfirm = useCallback(() => {
|
|
208
|
+
onChoose?.(orderByItems.length > 0 ? orderByItems : undefined);
|
|
209
|
+
onClose();
|
|
210
|
+
}, [orderByItems, onChoose, onClose]);
|
|
211
|
+
// =============================================================================
|
|
212
|
+
// RENDER
|
|
213
|
+
// =============================================================================
|
|
214
|
+
return (_jsxs(_Fragment, { children: [_jsxs(TMModal, { title: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QueryOrderBy}${orderByItems.length > 0 ? ` (${orderByItems.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: orderByItems.length === 0, onClick: () => setOrderByItems([]) })] }), _jsxs("div", { style: {
|
|
215
|
+
flex: 1,
|
|
216
|
+
overflow: 'auto',
|
|
217
|
+
border: `1px solid ${TMColors.border_normal}`,
|
|
218
|
+
borderRadius: '4px',
|
|
219
|
+
padding: '5px'
|
|
220
|
+
}, children: [isLoading && _jsxs("p", { style: { textAlign: 'center', padding: '20px' }, children: [SDKUI_Localizator.Loading, "..."] }), !isLoading && orderByItems.length === 0 && (_jsx(TMToppyMessage, { message: SDKUI_Localizator.NoSortingApplied, titleTooltip: SDKUI_Localizator.NoSortingApplied })), !isLoading && orderByItems.length > 0 && (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '2px' }, children: orderByItems.map((item, index) => {
|
|
221
|
+
const md = getMetadataForOrderByItem(item);
|
|
222
|
+
const isDragging = draggingItem === item;
|
|
223
|
+
return (_jsxs("div", { draggable: true, onDragStart: (e) => handleDragStart(e, item), onDragEnd: handleDragEnd, onDragOver: handleDragOver, onDrop: (e) => handleDrop(e, item), style: {
|
|
224
|
+
display: 'flex',
|
|
225
|
+
alignItems: 'center',
|
|
226
|
+
gap: '6px',
|
|
227
|
+
padding: '4px 8px',
|
|
228
|
+
backgroundColor: isDragging ? TMColors.primary_container : TMColors.default_background,
|
|
229
|
+
borderRadius: '3px',
|
|
230
|
+
border: `1px solid ${isDragging ? TMColors.primaryColor : TMColors.border_normal}`,
|
|
231
|
+
minHeight: '32px',
|
|
232
|
+
cursor: 'grab',
|
|
233
|
+
transition: 'all 0.2s ease',
|
|
234
|
+
transform: isDragging ? 'scale(1.02)' : 'scale(1)',
|
|
235
|
+
boxShadow: isDragging ? '0 4px 12px rgba(0,0,0,0.15)' : 'none',
|
|
236
|
+
opacity: isDragging ? 0.9 : 1,
|
|
237
|
+
}, 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(TMDropDown, { width: "175px", height: "28px", value: item.asc ? 'asc' : 'desc', dataSource: [
|
|
238
|
+
{ value: 'asc', display: SDKUI_Localizator.ValueAscending },
|
|
239
|
+
{ value: 'desc', display: SDKUI_Localizator.ValueDescending }
|
|
240
|
+
], onValueChanged: (e) => handleChangeAsc(index, e.target.value === 'asc') }) }), _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: () => handleRemoveOrderBy(index) }) })] }, `${item.tid}_${item.mid}_${index}`));
|
|
241
|
+
}) }))] })] })] }), showMetadataChooser && (_jsx(TMMetadataChooserForm, { allowMultipleSelection: true, height: "600px", width: "700px", allowSysMetadata: allowSysMetadata, filterMetadata: filterMetadata, qd: qd, selectedIDs: selectedIDs, onClose: handleCloseChooser, onChoose: handleChooseFromChooser }))] }));
|
|
242
|
+
};
|
|
243
|
+
export default TMMetadataSorterForm;
|