@topconsultnpm/sdkui-react 6.21.0-dev3.16 → 6.21.0-dev3.18
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/FloatingMenuBar/styles.d.ts +4 -4
- package/lib/components/base/TMDataGrid.js +12 -2
- package/lib/components/base/TMModal.js +2 -2
- 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 +208 -58
- package/lib/components/features/search/metadataFormHelper.d.ts +16 -0
- package/lib/components/features/search/metadataFormHelper.js +77 -0
- package/lib/helper/SDKUI_Localizator.d.ts +3 -0
- package/lib/helper/SDKUI_Localizator.js +30 -0
- package/package.json +1 -1
|
@@ -73,20 +73,20 @@ export declare const MenuButton: import("styled-components/dist/types").IStyledC
|
|
|
73
73
|
export declare const ConfigButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never> & Partial<Pick<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>>> & string;
|
|
74
74
|
export declare const ApplyButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never> & Partial<Pick<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>>> & string;
|
|
75
75
|
export declare const CloseButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never> & Partial<Pick<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, never>>> & string;
|
|
76
|
-
export declare const ContextMenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("styled-components").FastOmit<import("styled-components").FastOmit<import("
|
|
76
|
+
export declare const ContextMenuButton: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<Omit<import("styled-components").FastOmit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
|
|
77
77
|
$isActive?: boolean;
|
|
78
78
|
}, never> & Partial<Pick<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
|
|
79
79
|
$isActive?: boolean;
|
|
80
|
-
}, never
|
|
80
|
+
}, never>> & {
|
|
81
81
|
as?: import("styled-components").WebTarget | undefined;
|
|
82
82
|
forwardedAs?: import("styled-components").WebTarget | undefined;
|
|
83
83
|
}, "ref"> & {
|
|
84
84
|
ref?: ((instance: HTMLButtonElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLButtonElement> | null | undefined;
|
|
85
|
-
}, never> & Partial<Pick<Omit<import("styled-components").FastOmit<import("styled-components").FastOmit<import("
|
|
85
|
+
}, never> & Partial<Pick<Omit<import("styled-components").FastOmit<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
|
|
86
86
|
$isActive?: boolean;
|
|
87
87
|
}, never> & Partial<Pick<import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "$isActive"> & {
|
|
88
88
|
$isActive?: boolean;
|
|
89
|
-
}, never
|
|
89
|
+
}, never>> & {
|
|
90
90
|
as?: import("styled-components").WebTarget | undefined;
|
|
91
91
|
forwardedAs?: import("styled-components").WebTarget | undefined;
|
|
92
92
|
}, "ref"> & {
|
|
@@ -36,6 +36,8 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
36
36
|
const [customContextMenuPosition, setCustomContextMenuPosition] = useState({ x: 0, y: 0 });
|
|
37
37
|
const [customContextMenuRowKey, setCustomContextMenuRowKey] = useState(undefined);
|
|
38
38
|
const gridContainerRef = useRef(null);
|
|
39
|
+
// Ref per evitare focus ripetuti sulla search panel
|
|
40
|
+
const searchPanelFocusedRef = useRef(false);
|
|
39
41
|
useEffect(() => {
|
|
40
42
|
const count = getRecordCount(dataSource);
|
|
41
43
|
setTotalRecordCount(count);
|
|
@@ -44,10 +46,15 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
44
46
|
useEffect(() => {
|
|
45
47
|
if (!searchPanelFocusTrigger || searchPanelFocusTrigger <= 0 || !showSearchPanel || !internalRef.current)
|
|
46
48
|
return;
|
|
49
|
+
// Reset del flag quando cambia il trigger (per permettere un nuovo focus se richiesto)
|
|
50
|
+
searchPanelFocusedRef.current = false;
|
|
47
51
|
setTimeout(() => {
|
|
52
|
+
if (searchPanelFocusedRef.current)
|
|
53
|
+
return;
|
|
48
54
|
const searchInput = internalRef.current?.instance().element().querySelector('.dx-datagrid-search-panel input');
|
|
49
55
|
if (searchInput) {
|
|
50
56
|
searchInput.focus();
|
|
57
|
+
searchPanelFocusedRef.current = true;
|
|
51
58
|
}
|
|
52
59
|
}, 100);
|
|
53
60
|
}, [searchPanelFocusTrigger, showSearchPanel]);
|
|
@@ -305,13 +312,16 @@ const TMDataGrid = React.forwardRef((props, ref) => {
|
|
|
305
312
|
return;
|
|
306
313
|
// Update state with the current number of visible rows in the DataGrid
|
|
307
314
|
setVisibleItemsCount(internalRef.current.instance()?.getVisibleRows()?.length ?? 0);
|
|
308
|
-
// Focusing SearchPanel on content ready
|
|
309
|
-
if (showSearchPanel && searchPanelFocusTrigger && searchPanelFocusTrigger > 0) {
|
|
315
|
+
// Focusing SearchPanel on content ready - solo se non è già stato fatto
|
|
316
|
+
if (showSearchPanel && searchPanelFocusTrigger && searchPanelFocusTrigger > 0 && !searchPanelFocusedRef.current) {
|
|
310
317
|
// Use a small delay to ensure the DOM is fully rendered before trying to focus
|
|
311
318
|
setTimeout(() => {
|
|
319
|
+
if (searchPanelFocusedRef.current)
|
|
320
|
+
return;
|
|
312
321
|
const searchInput = internalRef.current?.instance().element().querySelector('.dx-datagrid-search-panel input');
|
|
313
322
|
if (searchInput) {
|
|
314
323
|
searchInput.focus();
|
|
324
|
+
searchPanelFocusedRef.current = true;
|
|
315
325
|
}
|
|
316
326
|
}, 100);
|
|
317
327
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useState, useEffect, useRef } from 'react';
|
|
3
3
|
import { Popup } from 'devextreme-react';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
|
-
import
|
|
5
|
+
import { TMCard } from './TMLayout';
|
|
6
6
|
import { FontSize, TMColors } from '../../utils/theme';
|
|
7
7
|
import { IconWindowMaximize, IconWindowMinimize, svgToString } from '../../helper';
|
|
8
8
|
const StyledModal = styled.div `
|
|
@@ -110,6 +110,6 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
110
110
|
onClick: () => setIsFullScreen(!isFullScreen)
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
] : undefined, children: _jsxs(
|
|
113
|
+
] : undefined, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }, children: [toolbar && (_jsx(StyledModalToolbar, { children: toolbar })), _jsx("div", { style: { flex: 1, overflow: 'hidden', minHeight: 0 }, children: _jsx(TMCard, { showBorder: false, padding: false, scrollY: true, children: children }) })] }) })) : (_jsxs(StyledModal, { "$isModal": isModal, className: "temp-modal", "$fontSize": fontSize, "$width": initialWidth, "$height": initialHeight, children: [toolbar ? _jsx(StyledModalToolbar, { children: toolbar }) : _jsx(_Fragment, {}), _jsx(StyledModalContext, { children: children })] })) }));
|
|
114
114
|
};
|
|
115
115
|
export default TMModal;
|
|
@@ -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, { 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, { 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;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { PlatformObjectValidator, QueryDescriptor, WhereItem, SDK_Localizator,
|
|
3
|
+
import { PlatformObjectValidator, QueryDescriptor, WhereItem, SDK_Localizator, SelectItemVisibilities, SDK_Globals, SavedQueryCacheService, SearchEngine, QueryOperators, AccessLevelsEx, AccessLevels } from '@topconsultnpm/sdk-ts';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
5
|
import TMSearchQueryEditor from './TMSearchQueryEditor';
|
|
6
|
-
import { getDcmtTypesByQdAsync, SDKUI_Localizator, getQD, IconMenuVertical, IconAddCircleOutline, IconEdit, IconEasy, IconAdvanced, deepCompare, IconSearch, IconClear, getDefaultOperator, prepareQdForSearchAsync, IsParametricQuery, SDKUI_Globals, IconArrowRight, IconMenuCAArchive, getListMaxItems, DEFAULT_MAX_DCMTS_TO_BE_RETURNED } from '../../../helper';
|
|
6
|
+
import { getDcmtTypesByQdAsync, SDKUI_Localizator, getQD, IconMenuVertical, IconAddCircleOutline, IconEdit, IconEasy, IconAdvanced, deepCompare, IconSearch, IconClear, getDefaultOperator, prepareQdForSearchAsync, IsParametricQuery, SDKUI_Globals, IconArrowRight, IconMenuCAArchive, getListMaxItems, DEFAULT_MAX_DCMTS_TO_BE_RETURNED, IconSortAsc, IconSortDesc, IconFilter, IconColumns, IconSort } from '../../../helper';
|
|
7
7
|
import { useQueryParametersDialog } from '../../../hooks/useQueryParametersDialog';
|
|
8
8
|
import { FormModes } from '../../../ts';
|
|
9
9
|
import { TMColors } from '../../../utils/theme';
|
|
@@ -21,6 +21,9 @@ import TMSavedQueryForm from './TMSavedQueryForm';
|
|
|
21
21
|
import { ContextMenu } from '../../NewComponents/ContextMenu';
|
|
22
22
|
import { AdvancedMenuButtons } from '../../editors/TMMetadataValues';
|
|
23
23
|
import TMToppyMessage from '../../../helper/TMToppyMessage';
|
|
24
|
+
import TMMetadataSorterForm from './TMMetadataSorterForm';
|
|
25
|
+
import TMMetadataOutputForm from './TMMetadataOutputForm';
|
|
26
|
+
import TMTooltip from '../../base/TMTooltip';
|
|
24
27
|
const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, SQD, inputMids, onSearchCompleted, onSqdSaved, onBack, onClosePanel, allowMaximize = true, onMaximizePanel, onBackToResult, passToArchiveCallback, maxDcmtsToBeReturned }) => {
|
|
25
28
|
const [confirmQueryParams, ConfirmQueryParamsDialog] = useQueryParametersDialog();
|
|
26
29
|
const [qd, setQd] = useState();
|
|
@@ -34,7 +37,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
34
37
|
const [showAdvancedSearch, setShowAdvancedSearch] = useState(false);
|
|
35
38
|
const [showFiltersConfig, setShowFiltersConfig] = useState(false);
|
|
36
39
|
const [showOutputConfig, setShowOutputConfig] = useState(false);
|
|
37
|
-
const [
|
|
40
|
+
const [showMetadataSorterForm, setShowMetadataSorterForm] = useState(false);
|
|
38
41
|
const [showDistinctValuesPanel, setShowDistinctValuesPanel] = useState(false);
|
|
39
42
|
const [focusedTidMid, setFocusedTidMid] = useState();
|
|
40
43
|
const [modalIsOpen, setModalIsOpen] = useState(false);
|
|
@@ -162,7 +165,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
162
165
|
newWi.init({ ...curItem, value1: undefined, value2: undefined });
|
|
163
166
|
return newWi;
|
|
164
167
|
});
|
|
165
|
-
setQd({ ...qd, where: newWhere });
|
|
168
|
+
setQd({ ...qd, where: newWhere, orderBy: [] });
|
|
166
169
|
};
|
|
167
170
|
const searchAsync = async (qdInput, isAdvancedSearch) => {
|
|
168
171
|
onSearchCompleted?.([], undefined); // reset results
|
|
@@ -266,61 +269,16 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
266
269
|
setQd({ ...qd, where: newWhere });
|
|
267
270
|
}, [qd, fromDTD?.metadata, SQD?.masterTID]);
|
|
268
271
|
const handleCloseOutputConfig = useCallback(() => setShowOutputConfig(false), []);
|
|
269
|
-
const handleChooseOutput = useCallback((tid_mids) => {
|
|
270
|
-
if (!fromDTD?.metadata)
|
|
271
|
-
return;
|
|
272
|
-
if (!tid_mids)
|
|
273
|
-
return;
|
|
274
|
-
// copia dei SelectItems senza i rimossi
|
|
275
|
-
let newSelect = qd?.select?.filter(item => tid_mids?.some(tm => tm.mid == item.mid)) ?? [];
|
|
276
|
-
// aggiungiamo i nuovi
|
|
277
|
-
for (const tm of tid_mids.filter(tm => !qd?.select?.some(item => item.mid == tm.mid))) {
|
|
278
|
-
let md = fromDTD?.metadata.find(o => o.id == tm.mid);
|
|
279
|
-
let si = new SelectItem();
|
|
280
|
-
si.visibility = SelectItemVisibilities.Visible;
|
|
281
|
-
si.tid = SQD?.masterTID;
|
|
282
|
-
si.mid = md?.id;
|
|
283
|
-
let indexMD = 0;
|
|
284
|
-
for (const m of fromDTD?.metadata ?? []) {
|
|
285
|
-
if (m.id == si.mid)
|
|
286
|
-
break;
|
|
287
|
-
if (newSelect.findIndex(o => o.mid == m.id && o.tid == SQD?.masterTID) < 0)
|
|
288
|
-
continue;
|
|
289
|
-
indexMD++;
|
|
290
|
-
}
|
|
291
|
-
newSelect.splice(indexMD, 0, si);
|
|
292
|
-
}
|
|
293
|
-
setQd({ ...qd, select: newSelect });
|
|
294
|
-
}, [qd, fromDTD?.metadata, SQD?.masterTID]);
|
|
295
|
-
const handleCloseOrderByConfig = useCallback(() => setShowOrderByConfig(false), []);
|
|
296
|
-
const handleChooseOrderBy = useCallback((tid_mids) => {
|
|
297
|
-
if (!fromDTD?.metadata)
|
|
298
|
-
return;
|
|
299
|
-
if (!tid_mids)
|
|
300
|
-
return;
|
|
301
|
-
// copia dei OrderByItems senza i rimossi
|
|
302
|
-
let newOrderBy = qd?.orderBy?.filter(item => tid_mids?.some(tm => tm.mid == item.mid)) ?? [];
|
|
303
|
-
// aggiungiamo i nuovi
|
|
304
|
-
for (const tm of tid_mids.filter(tm => !qd?.orderBy?.some(item => item.mid == tm.mid))) {
|
|
305
|
-
let md = fromDTD?.metadata.find(o => o.id == tm.mid);
|
|
306
|
-
let oi = new OrderByItem();
|
|
307
|
-
oi.tid = SQD?.masterTID;
|
|
308
|
-
oi.mid = md?.id;
|
|
309
|
-
oi.asc = true;
|
|
310
|
-
newOrderBy.push(oi);
|
|
311
|
-
}
|
|
312
|
-
setQd({ ...qd, orderBy: newOrderBy });
|
|
313
|
-
}, [qd, fromDTD?.metadata, SQD?.masterTID]);
|
|
314
272
|
const contextMenuItems = useMemo(() => [
|
|
315
273
|
...(showBackToResultButton ? [{ icon: _jsx(IconArrowRight, {}), name: "Vai a risultato", onClick: () => { onBackToResult?.(); } }] : []),
|
|
316
274
|
{ icon: _jsx(IconAddCircleOutline, {}), name: SDKUI_Localizator.SavedQueryNew, beginGroup: showBackToResultButton, onClick: () => { openSqdForm(FormModes.Create); } },
|
|
317
275
|
{ icon: _jsx(IconEdit, {}), name: SDKUI_Localizator.SavedQueryUpdate, disabled: (SQD && SQD.id == 1), onClick: () => { openSqdForm(FormModes.Update); } },
|
|
318
276
|
{ icon: showAdvancedSearch ? _jsx(IconEasy, {}) : _jsx(IconAdvanced, {}), beginGroup: true, name: showAdvancedSearch ? SDKUI_Localizator.Search_Easy : SDKUI_Localizator.Search_Advanced, onClick: () => { changeAdvancedSearchAsync(!showAdvancedSearch); } },
|
|
319
|
-
{ icon: _jsx(
|
|
320
|
-
{ icon: _jsx(
|
|
321
|
-
{ icon: _jsx(
|
|
277
|
+
{ icon: _jsx(IconFilter, {}), name: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QueryWhere}`, beginGroup: true, onClick: () => { setShowFiltersConfig(true); } },
|
|
278
|
+
{ icon: _jsx(IconColumns, {}), name: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QuerySelect}`, onClick: () => { setShowOutputConfig(true); } },
|
|
279
|
+
{ icon: _jsx(IconSort, {}), name: `${SDKUI_Localizator.Configure} - ${SDK_Localizator.QueryOrderBy}`, onClick: () => { setShowMetadataSorterForm(true); } },
|
|
322
280
|
{ icon: _jsx(IconMenuCAArchive, { viewBox: '11 11.5 26 27', fontSize: 16, strokeWidth: 2, color: 'black' }), beginGroup: true, name: SDKUI_Localizator.PassToArchive, disabled: fromDTD?.perm?.canArchive !== AccessLevelsEx.Yes && fromDTD?.perm?.canArchive !== AccessLevelsEx.Mixed, onClick: handlePassToArchive }
|
|
323
|
-
], [showBackToResultButton, showAdvancedSearch, SQD, onBackToResult, openSqdForm, changeAdvancedSearchAsync, setShowFiltersConfig, setShowOutputConfig,
|
|
281
|
+
], [showBackToResultButton, showAdvancedSearch, SQD, onBackToResult, openSqdForm, changeAdvancedSearchAsync, setShowFiltersConfig, setShowOutputConfig, handlePassToArchive, fromDTD?.perm?.canArchive]);
|
|
324
282
|
const captionText = showAllMdWhere ? SDKUI_Localizator.ShowLess : SDKUI_Localizator.ShowAll;
|
|
325
283
|
let maxItems = getListMaxItems(deviceType ?? DeviceType.DESKTOP);
|
|
326
284
|
const diff = (qd?.where?.length ?? 0) - maxItems;
|
|
@@ -329,7 +287,25 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
329
287
|
: _jsx(_Fragment, {}) }), children: [_jsx(ConfirmQueryParamsDialog, {}), SQD
|
|
330
288
|
? _jsxs("div", { onContextMenu: (e) => e.preventDefault(), style: { height: '100%', width: '100%', position: 'relative', display: showSqdForm ? 'none' : 'flex', flexDirection: 'column', gap: 5 }, children: [showAdvancedSearch
|
|
331
289
|
? _jsx(TMQueryEditor, { formMode: FormModes.Update, showToolbar: false, inputData: qd, validateSelect: true, showApply: false, onQDChanged: handleQdChanged, updateIsModalOpen: updateIsModalOpen })
|
|
332
|
-
: _jsx(TMSearchQueryEditor, { qd: qd, dcmtTypesList: dcmtTypesList, isExpertMode: isExpertMode, showAllMdWhere: showAllMdWhere, onQdChanged: handleQdChanged, onFocusedMetadataChanged: setFocusedTidMid, onAdvancedMenuClick: handleAdvancedMenuClick, updateIsModalOpen: updateIsModalOpen }), _jsxs("
|
|
290
|
+
: _jsx(TMSearchQueryEditor, { qd: qd, dcmtTypesList: dcmtTypesList, isExpertMode: isExpertMode, showAllMdWhere: showAllMdWhere, onQdChanged: handleQdChanged, onFocusedMetadataChanged: setFocusedTidMid, onAdvancedMenuClick: handleAdvancedMenuClick, updateIsModalOpen: updateIsModalOpen }), qd?.select && qd.select.length > 0 && (_jsxs(StyledSelectBadgesContainer, { children: [_jsxs(StyledSelectLabel, { children: [SDK_Localizator.QuerySelect, ":"] }), _jsx(StyledSelectBadgesWrapper, { children: (() => {
|
|
291
|
+
const maxVisible = isMobile ? 1 : 4;
|
|
292
|
+
const visibleItems = qd.select.slice(0, maxVisible);
|
|
293
|
+
const hiddenCount = qd.select.length - maxVisible;
|
|
294
|
+
return (_jsxs(_Fragment, { children: [visibleItems.map((si, index) => {
|
|
295
|
+
const md = fromDTD?.metadata?.find(m => m.id === si.mid);
|
|
296
|
+
const mdName = md?.nameLoc ?? md?.name ?? `MID ${si.mid}`;
|
|
297
|
+
return (_jsxs(StyledSelectBadge, { children: [_jsx(IconColumns, { fontSize: 12 }), _jsx("span", { className: "badge-name", children: mdName })] }, `${si.tid}-${si.mid}-${index}`));
|
|
298
|
+
}), hiddenCount > 0 && (_jsxs(StyledSelectBadgeMore, { onClick: () => setShowOutputConfig(true), children: ["+", hiddenCount] }))] }));
|
|
299
|
+
})() }), _jsx(StyledSelectEditButton, { title: SDKUI_Localizator.Configure, onClick: () => setShowOutputConfig(true), children: _jsx(IconEdit, { fontSize: 16 }) })] })), qd?.orderBy && qd.orderBy.length > 0 && (_jsxs(StyledOrderByBadgesContainer, { children: [_jsxs(StyledOrderByLabel, { children: [qd.orderBy.length === 1 ? SDKUI_Localizator.SortBy : SDKUI_Localizator.SortByPlural, ":"] }), _jsx(StyledOrderByBadgesWrapper, { children: (() => {
|
|
300
|
+
const maxVisible = isMobile ? 1 : 4;
|
|
301
|
+
const visibleItems = qd.orderBy.slice(0, maxVisible);
|
|
302
|
+
const hiddenCount = qd.orderBy.length - maxVisible;
|
|
303
|
+
return (_jsxs(_Fragment, { children: [visibleItems.map((obi, index) => {
|
|
304
|
+
const md = fromDTD?.metadata?.find(m => m.id === obi.mid);
|
|
305
|
+
const mdName = md?.nameLoc ?? md?.name ?? `MID ${obi.mid}`;
|
|
306
|
+
return (_jsxs(StyledOrderByBadge, { children: [_jsx(TMTooltip, { content: obi.asc !== false ? SDKUI_Localizator.ValueAscending : SDKUI_Localizator.ValueDescending, children: obi.asc !== false ? (_jsx(IconSortAsc, { fontSize: 12 })) : (_jsx(IconSortDesc, { fontSize: 12 })) }), _jsx("span", { className: "badge-name", children: mdName })] }, `${obi.tid}-${obi.mid}-${index}`));
|
|
307
|
+
}), hiddenCount > 0 && (_jsxs(StyledOrderByBadgeMore, { onClick: () => setShowMetadataSorterForm(true), children: ["+", hiddenCount] }))] }));
|
|
308
|
+
})() }), _jsx(StyledOrderByEditButton, { title: SDKUI_Localizator.Configure, onClick: () => setShowMetadataSorterForm(true), children: _jsx(IconEdit, { fontSize: 16 }) })] })), _jsxs("div", { style: {
|
|
333
309
|
display: 'flex',
|
|
334
310
|
flexWrap: 'wrap',
|
|
335
311
|
justifyContent: 'center',
|
|
@@ -338,8 +314,12 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
338
314
|
width: '100%'
|
|
339
315
|
}, children: [_jsx(TMButton, { btnStyle: 'advanced', icon: _jsx(IconSearch, {}), showTooltip: false, width: '90px', caption: SDKUI_Localizator.Search, advancedColor: '#4A96D2', onClick: handleSearchButtonClick }), _jsx(TMButton, { width: '90px', btnStyle: 'advanced', advancedType: 'primary', showTooltip: false, caption: SDKUI_Localizator.Clear, icon: _jsx(IconClear, {}), advancedColor: 'white', color: 'primaryOutline', onClick: clearFilters }), (!showAdvancedSearch && qd?.where && qd.where.length > initialMaxItems) && (_jsx(TMButton, { width: '120px', btnStyle: isMobile ? 'icon' : 'advanced', advancedColor: TMColors.button_primary, caption: captionText, showTooltip: false, icon: isMobile ? (_jsx("div", { children: _jsx("p", { children: showAllMdWhere ? `-${diff}` : `+${diff}` }) })) : (_jsx("p", { children: showAllMdWhere ? `-${diff}` : `+${diff}` })), onClick: () => setShowAllMdWhere(!showAllMdWhere) }))] }), showFiltersConfig &&
|
|
340
316
|
_jsx(TMMetadataChooserForm, { allowMultipleSelection: true, height: '500px', width: '600px', allowSysMetadata: true, filterMetadata: (o => o.perm?.canSearch === AccessLevels.Yes), qd: qd, selectedIDs: qd?.where?.map((w) => ({ tid: w.tid, mid: w.mid })), onClose: handleCloseFiltersConfig, onChoose: handleChooseFilters }), showOutputConfig &&
|
|
341
|
-
_jsx(
|
|
342
|
-
|
|
317
|
+
_jsx(TMMetadataOutputForm, { qd: qd, selectedSelectItems: qd?.select, allowSysMetadata: true, filterMetadata: (o => o.perm?.canView === AccessLevels.Yes || o.perm?.canUpdate === AccessLevels.Yes), onClose: handleCloseOutputConfig, onChoose: (selectItems) => {
|
|
318
|
+
setQd({ ...qd, select: selectItems });
|
|
319
|
+
} }), showMetadataSorterForm &&
|
|
320
|
+
_jsx(TMMetadataSorterForm, { qd: qd, selectedOrderByItems: qd?.orderBy, allowSysMetadata: true, filterMetadata: (o => o.perm?.canView === AccessLevels.Yes || o.perm?.canUpdate === AccessLevels.Yes), onClose: () => setShowMetadataSorterForm(false), onChoose: (orderByItems) => {
|
|
321
|
+
setQd({ ...qd, orderBy: orderByItems });
|
|
322
|
+
} })] })
|
|
343
323
|
:
|
|
344
324
|
_jsx(TMToppyMessage, { message: SDKUI_Localizator.DcmtTypeSelectOrQuickSearch, titleTooltip: SDKUI_Localizator.DcmtTypeSelectOrQuickSearch }), showSqdForm &&
|
|
345
325
|
_jsx(StyledModalContainer, { style: { backgroundColor: `${TMColors.backgroundColorHeader}12` }, children: _jsx(TMSavedQueryForm, { height: '50%', width: '50%', id: formModeSqdForm === FormModes.Create ? -1 : SQD?.id, title: 'Ricerca rapida', formMode: formModeSqdForm, showBackButton: true, qd: qd, isAdvancedSearch: showAdvancedSearch, isModal: false, onClose: () => { setShowSqdForm(false); }, onSaved: onSqdSaved }) })] }), showDistinctValuesPanel &&
|
|
@@ -415,6 +395,174 @@ export const refreshLastSearch = async (qd) => {
|
|
|
415
395
|
}
|
|
416
396
|
return searchResults;
|
|
417
397
|
};
|
|
398
|
+
// =============================================================================
|
|
399
|
+
// STYLED COMPONENTS per i badge degli ordinamenti
|
|
400
|
+
// =============================================================================
|
|
401
|
+
const StyledOrderByBadgesContainer = styled.div `
|
|
402
|
+
display: flex;
|
|
403
|
+
flex-wrap: wrap;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 8px;
|
|
406
|
+
padding: 6px 10px;
|
|
407
|
+
margin: 1px 0;
|
|
408
|
+
background: linear-gradient(135deg, rgba(74, 150, 210, 0.08) 0%, rgba(37, 89, 165, 0.08) 100%);
|
|
409
|
+
border-radius: 8px;
|
|
410
|
+
border: 1px solid rgba(74, 150, 210, 0.2);
|
|
411
|
+
`;
|
|
412
|
+
const StyledOrderByLabel = styled.span `
|
|
413
|
+
font-size: 0.82rem;
|
|
414
|
+
font-weight: 600;
|
|
415
|
+
color: #4A96D2;
|
|
416
|
+
white-space: nowrap;
|
|
417
|
+
`;
|
|
418
|
+
const StyledOrderByBadgesWrapper = styled.div `
|
|
419
|
+
display: flex;
|
|
420
|
+
flex-wrap: wrap;
|
|
421
|
+
gap: 6px;
|
|
422
|
+
align-items: center;
|
|
423
|
+
flex: 1;
|
|
424
|
+
`;
|
|
425
|
+
const StyledOrderByBadge = styled.div `
|
|
426
|
+
display: inline-flex;
|
|
427
|
+
align-items: center;
|
|
428
|
+
gap: 4px;
|
|
429
|
+
padding: 3px 8px;
|
|
430
|
+
background: white;
|
|
431
|
+
border: 1px solid #d1d9e6;
|
|
432
|
+
border-radius: 16px;
|
|
433
|
+
font-size: 0.82rem;
|
|
434
|
+
color: #333;
|
|
435
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
436
|
+
|
|
437
|
+
.badge-name {
|
|
438
|
+
max-width: 120px;
|
|
439
|
+
overflow: hidden;
|
|
440
|
+
text-overflow: ellipsis;
|
|
441
|
+
white-space: nowrap;
|
|
442
|
+
|
|
443
|
+
@media (max-width: 600px) {
|
|
444
|
+
max-width: 80px;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
`;
|
|
448
|
+
const StyledOrderByBadgeMore = styled.div `
|
|
449
|
+
display: inline-flex;
|
|
450
|
+
align-items: center;
|
|
451
|
+
justify-content: center;
|
|
452
|
+
padding: 3px 10px;
|
|
453
|
+
background: linear-gradient(135deg, #4A96D2 0%, #2559A5 100%);
|
|
454
|
+
border: none;
|
|
455
|
+
border-radius: 16px;
|
|
456
|
+
font-size: 0.82rem;
|
|
457
|
+
font-weight: 600;
|
|
458
|
+
color: white;
|
|
459
|
+
cursor: pointer;
|
|
460
|
+
transition: all 0.2s ease;
|
|
461
|
+
|
|
462
|
+
&:hover {
|
|
463
|
+
transform: scale(1.05);
|
|
464
|
+
box-shadow: 0 2px 6px rgba(74, 150, 210, 0.4);
|
|
465
|
+
}
|
|
466
|
+
`;
|
|
467
|
+
const StyledOrderByEditButton = styled.div `
|
|
468
|
+
display: flex;
|
|
469
|
+
align-items: center;
|
|
470
|
+
justify-content: center;
|
|
471
|
+
width: 28px;
|
|
472
|
+
height: 28px;
|
|
473
|
+
border-radius: 4px;
|
|
474
|
+
color: #4A96D2;
|
|
475
|
+
cursor: pointer;
|
|
476
|
+
transition: all 0.2s ease;
|
|
477
|
+
|
|
478
|
+
&:hover {
|
|
479
|
+
background-color: rgba(74, 150, 210, 0.15);
|
|
480
|
+
}
|
|
481
|
+
`;
|
|
482
|
+
// =============================================================================
|
|
483
|
+
// STYLED COMPONENTS per i badge degli output (select) - colore verde/teal
|
|
484
|
+
// =============================================================================
|
|
485
|
+
const StyledSelectBadgesContainer = styled.div `
|
|
486
|
+
display: flex;
|
|
487
|
+
flex-wrap: wrap;
|
|
488
|
+
align-items: center;
|
|
489
|
+
gap: 8px;
|
|
490
|
+
padding: 6px 10px;
|
|
491
|
+
margin: 1px 0;
|
|
492
|
+
background: linear-gradient(135deg, rgba(46, 125, 50, 0.08) 0%, rgba(27, 94, 32, 0.08) 100%);
|
|
493
|
+
border-radius: 8px;
|
|
494
|
+
border: 1px solid rgba(46, 125, 50, 0.2);
|
|
495
|
+
`;
|
|
496
|
+
const StyledSelectLabel = styled.span `
|
|
497
|
+
font-size: 0.82rem;
|
|
498
|
+
font-weight: 600;
|
|
499
|
+
color: #2E7D32;
|
|
500
|
+
white-space: nowrap;
|
|
501
|
+
`;
|
|
502
|
+
const StyledSelectBadgesWrapper = styled.div `
|
|
503
|
+
display: flex;
|
|
504
|
+
flex-wrap: wrap;
|
|
505
|
+
gap: 6px;
|
|
506
|
+
align-items: center;
|
|
507
|
+
flex: 1;
|
|
508
|
+
`;
|
|
509
|
+
const StyledSelectBadge = styled.div `
|
|
510
|
+
display: inline-flex;
|
|
511
|
+
align-items: center;
|
|
512
|
+
gap: 4px;
|
|
513
|
+
padding: 3px 8px;
|
|
514
|
+
background: white;
|
|
515
|
+
border: 1px solid #c8e6c9;
|
|
516
|
+
border-radius: 16px;
|
|
517
|
+
font-size: 0.82rem;
|
|
518
|
+
color: #333;
|
|
519
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
520
|
+
|
|
521
|
+
.badge-name {
|
|
522
|
+
max-width: 120px;
|
|
523
|
+
overflow: hidden;
|
|
524
|
+
text-overflow: ellipsis;
|
|
525
|
+
white-space: nowrap;
|
|
526
|
+
|
|
527
|
+
@media (max-width: 600px) {
|
|
528
|
+
max-width: 80px;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
`;
|
|
532
|
+
const StyledSelectBadgeMore = styled.div `
|
|
533
|
+
display: inline-flex;
|
|
534
|
+
align-items: center;
|
|
535
|
+
justify-content: center;
|
|
536
|
+
padding: 3px 10px;
|
|
537
|
+
background: linear-gradient(135deg, #43A047 0%, #2E7D32 100%);
|
|
538
|
+
border: none;
|
|
539
|
+
border-radius: 16px;
|
|
540
|
+
font-size: 0.82rem;
|
|
541
|
+
font-weight: 600;
|
|
542
|
+
color: white;
|
|
543
|
+
cursor: pointer;
|
|
544
|
+
transition: all 0.2s ease;
|
|
545
|
+
|
|
546
|
+
&:hover {
|
|
547
|
+
transform: scale(1.05);
|
|
548
|
+
box-shadow: 0 2px 6px rgba(46, 125, 50, 0.4);
|
|
549
|
+
}
|
|
550
|
+
`;
|
|
551
|
+
const StyledSelectEditButton = styled.div `
|
|
552
|
+
display: flex;
|
|
553
|
+
align-items: center;
|
|
554
|
+
justify-content: center;
|
|
555
|
+
width: 28px;
|
|
556
|
+
height: 28px;
|
|
557
|
+
border-radius: 4px;
|
|
558
|
+
color: #2E7D32;
|
|
559
|
+
cursor: pointer;
|
|
560
|
+
transition: all 0.2s ease;
|
|
561
|
+
|
|
562
|
+
&:hover {
|
|
563
|
+
background-color: rgba(46, 125, 50, 0.15);
|
|
564
|
+
}
|
|
565
|
+
`;
|
|
418
566
|
export const StyledToppyTextContainer = styled.div `
|
|
419
567
|
padding: 22px 8px;
|
|
420
568
|
width: 100%;
|
|
@@ -433,11 +581,13 @@ export const StyledToppyText = styled.p `
|
|
|
433
581
|
font-size: 1rem;
|
|
434
582
|
user-select: none;
|
|
435
583
|
-webkit-touch-callout: none;
|
|
436
|
-
|
|
584
|
+
-webkit-user-select: none;
|
|
585
|
+
-moz-user-select: none;
|
|
586
|
+
-ms-user-select: none;
|
|
437
587
|
margin: 0;
|
|
438
588
|
display: -webkit-box;
|
|
439
589
|
-webkit-box-orient: vertical;
|
|
440
|
-
-webkit-line-clamp: 3;
|
|
590
|
+
-webkit-line-clamp: 3;
|
|
441
591
|
overflow: hidden;
|
|
442
592
|
text-overflow: ellipsis;
|
|
443
593
|
white-space: normal;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DcmtTypeDescriptor, MetadataDescriptor, QueryDescriptor } from "@topconsultnpm/sdk-ts";
|
|
2
|
+
export type MetadataDescriptorWithKey = MetadataDescriptor & {
|
|
3
|
+
uniqueKey: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function removeDuplicatesByTidMid<T extends {
|
|
6
|
+
tid?: number;
|
|
7
|
+
mid?: number;
|
|
8
|
+
}>(items: T[]): T[];
|
|
9
|
+
export declare function loadMetadataFromQd(qd: QueryDescriptor | undefined, filterMetadata?: (value: MetadataDescriptor, index: number, array: MetadataDescriptor[]) => unknown): Promise<{
|
|
10
|
+
metadata: MetadataDescriptorWithKey[];
|
|
11
|
+
dcmtTypes: DcmtTypeDescriptor[];
|
|
12
|
+
}>;
|
|
13
|
+
export declare function areArraysEqual<T extends {
|
|
14
|
+
tid?: number;
|
|
15
|
+
mid?: number;
|
|
16
|
+
}>(arr1: T[], arr2: T[], additionalCompare?: (a: T, b: T) => boolean): boolean;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { DcmtTypeDescriptor, DcmtTypeListCacheService, SDK_Localizator } from "@topconsultnpm/sdk-ts";
|
|
2
|
+
import { getTIDsByQd } from "../../../helper/queryHelper";
|
|
3
|
+
import { SDKUI_Localizator } from "../../../helper";
|
|
4
|
+
import TMSpinner from "../../base/TMSpinner";
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// HELPER: Rimuove duplicati dalla lista (basato su tid + mid)
|
|
7
|
+
// =============================================================================
|
|
8
|
+
export function removeDuplicatesByTidMid(items) {
|
|
9
|
+
const seen = new Set();
|
|
10
|
+
return items.filter(item => {
|
|
11
|
+
const key = `${item.tid}_${item.mid}`;
|
|
12
|
+
if (seen.has(key))
|
|
13
|
+
return false;
|
|
14
|
+
seen.add(key);
|
|
15
|
+
return true;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// FUNZIONE PRINCIPALE: Estrae i metadati dal QueryDescriptor
|
|
20
|
+
// =============================================================================
|
|
21
|
+
export async function loadMetadataFromQd(qd, filterMetadata) {
|
|
22
|
+
if (!qd)
|
|
23
|
+
return { metadata: [], dcmtTypes: [] };
|
|
24
|
+
const dtdList = [];
|
|
25
|
+
const metadata = [];
|
|
26
|
+
try {
|
|
27
|
+
const tidsFromQd = getTIDsByQd(qd);
|
|
28
|
+
const inputTIDs = tidsFromQd.map((item) => item.tid);
|
|
29
|
+
if (inputTIDs.length === 0)
|
|
30
|
+
return { metadata: [], dcmtTypes: [] };
|
|
31
|
+
TMSpinner.show({ description: `${SDKUI_Localizator.Loading} - ${SDK_Localizator.DcmtType} ...` });
|
|
32
|
+
const dtds = await DcmtTypeListCacheService.GetFromTIDsAsync(inputTIDs, true, undefined, false, false);
|
|
33
|
+
for (let i = 0; i < dtds.length; i++) {
|
|
34
|
+
const dtd = new DcmtTypeDescriptor();
|
|
35
|
+
dtd.init({ ...dtds[i] });
|
|
36
|
+
const alias = tidsFromQd?.[i]?.alias ?? '';
|
|
37
|
+
dtd.customData2 = alias;
|
|
38
|
+
dtd.metadata?.forEach((md) => {
|
|
39
|
+
const mdWithKey = md;
|
|
40
|
+
mdWithKey.customData1 = dtd.id;
|
|
41
|
+
mdWithKey.customData2 = alias;
|
|
42
|
+
mdWithKey.uniqueKey = `${dtd.id}_${md.id}_${alias}`;
|
|
43
|
+
});
|
|
44
|
+
const filteredMetadata = filterMetadata
|
|
45
|
+
? dtd.metadata?.filter(filterMetadata)
|
|
46
|
+
: dtd.metadata;
|
|
47
|
+
dtdList.push({ ...dtd, metadata: filteredMetadata });
|
|
48
|
+
}
|
|
49
|
+
for (const dtd of dtdList) {
|
|
50
|
+
if (!dtd.metadata)
|
|
51
|
+
continue;
|
|
52
|
+
for (const md of dtd.metadata) {
|
|
53
|
+
metadata.push(md);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { metadata, dcmtTypes: dtdList };
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
TMSpinner.hide();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// =============================================================================
|
|
63
|
+
// HELPER: Confronta due array per determinare se sono stati modificati
|
|
64
|
+
// =============================================================================
|
|
65
|
+
export function areArraysEqual(arr1, arr2, additionalCompare) {
|
|
66
|
+
if (arr1.length !== arr2.length)
|
|
67
|
+
return false;
|
|
68
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
69
|
+
const a = arr1[i];
|
|
70
|
+
const b = arr2[i];
|
|
71
|
+
if (a.tid !== b.tid || a.mid !== b.mid)
|
|
72
|
+
return false;
|
|
73
|
+
if (additionalCompare && !additionalCompare(a, b))
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
@@ -487,6 +487,8 @@ export declare class SDKUI_Localizator {
|
|
|
487
487
|
static get NoDataToDisplay(): "Keine Daten zum Anzeigen" | "No data to display" | "No hay datos para mostrar" | "Aucune donnée à afficher" | "Sem dados para exibir" | "Nessun dato da visualizzare";
|
|
488
488
|
static get NoDcmtFound(): "Kein Dokument gefunden" | "No documents found" | "Ningún documento encontrado" | "Pas de documents trouvés" | "Nenhum documento encontrado" | "Nessun documento trovato";
|
|
489
489
|
static get NoDcmtSelected(): string;
|
|
490
|
+
static get NoSortingApplied(): string;
|
|
491
|
+
static get NoOutputMetadata(): string;
|
|
490
492
|
static get NoDetailDocumentFoundForArchiving(): "Kein Detaildokument zur Archivierung gefunden." | "No detail document found for archiving." | "No se encontró ningún documento de detalle para archivar." | "Aucun document détail trouvé pour l'archivage." | "Nenhum documento de detalhe encontrado para arquivamento." | "Nessun documento di dettaglio trovato per l'archiviazione.";
|
|
491
493
|
static get NoDocumentMatchFound(): "Kein zugeordnetes Dokument gefunden." | "No document match found." | "No se encontró ningún documento emparejado." | "Aucun document associé trouvé." | "Nenhum documento correspondente encontrado." | "Nessun documento abbinato trovato.";
|
|
492
494
|
static get NoDocumentSelectedForManyToManyMatching(): "Kein Dokument für die Mehrfach-Zuordnung ausgewählt." | "No document selected for many-to-many matching." | "Ningún documento seleccionado para el emparejamiento de muchos a muchos." | "Aucun document sélectionné pour l'association plusieurs à plusieurs." | "Nenhum documento selecionado para correspondência de muitos para muitos." | "Nessun documento selezionato per l'abbinamento molti a molti.";
|
|
@@ -738,6 +740,7 @@ export declare class SDKUI_Localizator {
|
|
|
738
740
|
static get SignatureType(): string;
|
|
739
741
|
static get Size(): "Größe" | "Size" | "Dimensión" | "Dimension" | "Tamanho" | "Dimensione";
|
|
740
742
|
static get SortBy(): "Sortieren nach" | "Sort by" | "Ordenar por" | "Trier par" | "Ordinamento";
|
|
743
|
+
static get SortByPlural(): "Sortierungen" | "Sortings" | "Ordenaciones" | "Tris" | "Ordenações" | "Ordinamenti";
|
|
741
744
|
static get Source(): "Ursprung" | "Source" | "Origen" | "Origem" | "Origine";
|
|
742
745
|
static get SpecialOperators(): "Spezielle Operatoren" | "Special operators" | "Operadores especiales" | "Opérateurs spéciaux" | "Os operadores especiais" | "Operatori speciali";
|
|
743
746
|
static get StandardMode(): "Standardmodus" | "Standard mode" | "Modo estándar" | "Mode standard" | "Modo padrão" | "Modalità standard";
|
|
@@ -4830,6 +4830,26 @@ export class SDKUI_Localizator {
|
|
|
4830
4830
|
default: return "Nessun documento selezionato";
|
|
4831
4831
|
}
|
|
4832
4832
|
}
|
|
4833
|
+
static get NoSortingApplied() {
|
|
4834
|
+
switch (this._cultureID) {
|
|
4835
|
+
case CultureIDs.De_DE: return "Keine Sortierung angewendet";
|
|
4836
|
+
case CultureIDs.En_US: return "No sorting applied";
|
|
4837
|
+
case CultureIDs.Es_ES: return "Ningún ordenamiento aplicado";
|
|
4838
|
+
case CultureIDs.Fr_FR: return "Aucun tri appliqué";
|
|
4839
|
+
case CultureIDs.Pt_PT: return "Nenhuma ordenação aplicada";
|
|
4840
|
+
default: return "Nessun ordinamento applicato";
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
static get NoOutputMetadata() {
|
|
4844
|
+
switch (this._cultureID) {
|
|
4845
|
+
case CultureIDs.De_DE: return "Keine Ausgabemetadaten ausgewählt";
|
|
4846
|
+
case CultureIDs.En_US: return "No output metadata selected";
|
|
4847
|
+
case CultureIDs.Es_ES: return "Ningún metadato de salida seleccionado";
|
|
4848
|
+
case CultureIDs.Fr_FR: return "Aucune métadonnée de sortie sélectionnée";
|
|
4849
|
+
case CultureIDs.Pt_PT: return "Nenhum metadado de saída selecionado";
|
|
4850
|
+
default: return "Nessun metadato di output selezionato";
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4833
4853
|
static get NoDetailDocumentFoundForArchiving() {
|
|
4834
4854
|
switch (this._cultureID) {
|
|
4835
4855
|
case CultureIDs.De_DE: return "Kein Detaildokument zur Archivierung gefunden.";
|
|
@@ -7360,6 +7380,16 @@ export class SDKUI_Localizator {
|
|
|
7360
7380
|
default: return "Ordinamento";
|
|
7361
7381
|
}
|
|
7362
7382
|
}
|
|
7383
|
+
static get SortByPlural() {
|
|
7384
|
+
switch (this._cultureID) {
|
|
7385
|
+
case CultureIDs.De_DE: return "Sortierungen";
|
|
7386
|
+
case CultureIDs.En_US: return "Sortings";
|
|
7387
|
+
case CultureIDs.Es_ES: return "Ordenaciones";
|
|
7388
|
+
case CultureIDs.Fr_FR: return "Tris";
|
|
7389
|
+
case CultureIDs.Pt_PT: return "Ordenações";
|
|
7390
|
+
default: return "Ordinamenti";
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7363
7393
|
static get Source() {
|
|
7364
7394
|
switch (this._cultureID) {
|
|
7365
7395
|
case CultureIDs.De_DE: return "Ursprung";
|