@topconsultnpm/sdkui-react 6.22.0-dev2.16 → 6.22.0-dev2.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/base/TMEditorBase.d.ts +2 -0
- package/lib/components/base/TMTooltip.d.ts +2 -1
- package/lib/components/base/TMTooltip.js +23 -6
- package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
- package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
- package/lib/components/choosers/TMDistinctValues.js +147 -195
- package/lib/components/choosers/TMMetadataChooser.js +1 -1
- package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
- package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
- package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
- package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
- package/lib/components/editors/TMDropDown.d.ts +3 -0
- package/lib/components/editors/TMDropDown.js +197 -5
- package/lib/components/features/documents/TMDcmtForm.js +10 -2
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
- package/lib/components/features/search/TMSearchResult.js +55 -4
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
- package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
- package/lib/components/viewers/TMMidViewer.js +4 -1
- package/lib/components/viewers/TMTidViewer.js +25 -11
- package/lib/helper/SDKUI_Globals.d.ts +20 -0
- package/lib/helper/SDKUI_Globals.js +44 -0
- package/lib/helper/SDKUI_Localizator.d.ts +1 -0
- package/lib/helper/SDKUI_Localizator.js +10 -0
- package/lib/helper/dataGridSearchHelper.d.ts +28 -0
- package/lib/helper/dataGridSearchHelper.js +51 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.d.ts +2 -0
- package/lib/helper/queryHelper.js +3 -1
- package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
- package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
- package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
- package/lib/hooks/useCaseFlowApprove.js +157 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
- package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
- package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
- package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
- package/lib/hooks/useTMDistinctValuesSource.js +105 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/caseflow/CaseFlowService.d.ts +208 -0
- package/lib/services/caseflow/CaseFlowService.js +623 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
- package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
- package/lib/services/caseflow/caseFlowNameCache.js +116 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/index.js +4 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { ValidationItem } from "@topconsultnpm/sdk-ts";
|
|
2
3
|
export type Colors = 'error' | 'warning' | 'success' | 'info' | 'primary' | 'secondary' | 'tertiary' | 'standard';
|
|
3
4
|
type IconModes = 'outside' | 'inside' | 'none';
|
|
@@ -6,6 +7,7 @@ export type Sizes = 'small' | 'medium' | 'large';
|
|
|
6
7
|
export type TMEditorButtonData = {
|
|
7
8
|
icon: any;
|
|
8
9
|
text?: string;
|
|
10
|
+
tooltipContent?: React.ReactNode;
|
|
9
11
|
onClick?: () => void;
|
|
10
12
|
};
|
|
11
13
|
export interface ITMEditorBase {
|
|
@@ -9,6 +9,7 @@ interface ITMTooltipProps extends ITooltipOptions {
|
|
|
9
9
|
hideAfterDelay?: boolean;
|
|
10
10
|
parentStyle?: React.CSSProperties;
|
|
11
11
|
childStyle?: React.CSSProperties;
|
|
12
|
+
allowContentInteraction?: boolean;
|
|
12
13
|
}
|
|
13
|
-
declare const TMTooltip: ({ children, position, content, hideAfterDelay, parentStyle, childStyle }: ITMTooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare const TMTooltip: ({ children, position, content, hideAfterDelay, parentStyle, childStyle, allowContentInteraction }: ITMTooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
14
15
|
export default TMTooltip;
|
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Tooltip } from 'devextreme-react/tooltip';
|
|
4
4
|
import { genUniqueId } from '../../helper';
|
|
5
5
|
import { DeviceType, useDeviceType } from './TMDeviceProvider';
|
|
6
|
-
|
|
6
|
+
/** Tempo per spostare il mouse dall'elemento al contenuto del tooltip, quando è interattivo */
|
|
7
|
+
const HIDE_DELAY_MS = 400;
|
|
8
|
+
const TMTooltip = ({ children, position, content, hideAfterDelay, parentStyle, childStyle, allowContentInteraction = false }) => {
|
|
7
9
|
const [showTooltip, setShowTooltip] = useState(false);
|
|
8
10
|
const [id, setID] = useState('');
|
|
11
|
+
const hideTimerRef = useRef();
|
|
9
12
|
useEffect(() => { setID(genUniqueId()); }, [children]);
|
|
10
13
|
useEffect(() => {
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
const hide = () => setShowTooltip(false);
|
|
15
|
+
window.addEventListener('click', hide);
|
|
16
|
+
return () => window.removeEventListener('click', hide);
|
|
13
17
|
}, []);
|
|
18
|
+
useEffect(() => { return () => clearTimeout(hideTimerRef.current); }, []);
|
|
14
19
|
const deviceType = useDeviceType();
|
|
15
|
-
|
|
20
|
+
const cancelHide = () => clearTimeout(hideTimerRef.current);
|
|
21
|
+
const show = () => { cancelHide(); setShowTooltip(true); };
|
|
22
|
+
const hide = () => {
|
|
23
|
+
cancelHide();
|
|
24
|
+
if (!allowContentInteraction) {
|
|
25
|
+
setShowTooltip(false);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
hideTimerRef.current = setTimeout(() => setShowTooltip(false), HIDE_DELAY_MS);
|
|
29
|
+
};
|
|
30
|
+
return (_jsxs("div", { style: parentStyle, children: [_jsx("div", { id: `idContainerTooltip${id}`, style: { display: 'flex', alignItems: 'center', height: 'max-content', width: 'max-content', ...childStyle }, onMouseEnter: show, onMouseLeave: hide, onTouchStart: show, onClick: () => { if (deviceType === DeviceType.DESKTOP || !hideAfterDelay)
|
|
16
31
|
return; setTimeout(() => { setShowTooltip(false); }, 1500); }, onTouchEnd: () => { if (deviceType === DeviceType.DESKTOP || !hideAfterDelay)
|
|
17
32
|
return; setTimeout(() => { setShowTooltip(false); }, 1500); }, children: children }), content &&
|
|
18
|
-
_jsx(Tooltip, { target: `#idContainerTooltip${id}`, hideOnOutsideClick: true, visible: showTooltip, position: position ?? 'bottom', onHidden: () => setShowTooltip(false), children:
|
|
33
|
+
_jsx(Tooltip, { target: `#idContainerTooltip${id}`, hideOnOutsideClick: true, visible: showTooltip, position: position ?? 'bottom', onHidden: () => setShowTooltip(false), children: allowContentInteraction
|
|
34
|
+
? _jsx("div", { style: { pointerEvents: 'all' }, onMouseEnter: cancelHide, onMouseLeave: hide, children: content })
|
|
35
|
+
: content })] }));
|
|
19
36
|
};
|
|
20
37
|
export default TMTooltip;
|
|
@@ -3,11 +3,12 @@ import { useEffect, useState } from 'react';
|
|
|
3
3
|
import { AccessLevelsEx, DcmtTypeListCacheService, SDK_Globals, SDK_Localizator } from '@topconsultnpm/sdk-ts';
|
|
4
4
|
import TMSpinner from '../base/TMSpinner';
|
|
5
5
|
import { IconSearch, SDKUI_Localizator } from '../../helper';
|
|
6
|
+
import { FontSize } from '../../utils/theme';
|
|
6
7
|
import { StyledDivHorizontal } from '../base/Styled';
|
|
7
8
|
import TMTidViewer, { TMDcmtTypeIcon } from '../viewers/TMTidViewer';
|
|
8
9
|
import TMSummary from '../editors/TMSummary';
|
|
9
10
|
import TMChooserForm from '../forms/TMChooserForm';
|
|
10
|
-
const TMDcmtTypeChooser = ({ tmSession, dataSource, disabled, backgroundColor, filter, accessFilter = 'all', showEditButton = true, borderRadius = '4px', buttons = [], placeHolder = `<${SDKUI_Localizator.NoneSelection}>`, openEditorOnSummaryClick, showBorder = true, showId = false, elementStyle, allowMultipleSelection, ShowOnlyDcmtTypes, ShowOnlySAP, filterTemplateTID, values, isModifiedWhen, label, width, height, showClearButton = false, validationItems = [], onValueChanged, updateIsModalOpen }) => {
|
|
11
|
+
const TMDcmtTypeChooser = ({ tmSession, dataSource, disabled, backgroundColor, icon, filter, accessFilter = 'all', showEditButton = true, borderRadius = '4px', fontSize = FontSize.defaultFontSize, buttons = [], placeHolder = `<${SDKUI_Localizator.NoneSelection}>`, openEditorOnSummaryClick, showBorder = true, showId = false, elementStyle, allowMultipleSelection, ShowOnlyDcmtTypes, ShowOnlySAP, filterTemplateTID, values, isModifiedWhen, label, width, height, showClearButton = false, validationItems = [], onValueChanged, updateIsModalOpen }) => {
|
|
11
12
|
const [showChooser, setShowChooser] = useState(false);
|
|
12
13
|
useEffect(() => {
|
|
13
14
|
TMSpinner.show({ description: `${SDKUI_Localizator.Loading} - ${SDK_Localizator.ListDcmtTypeOrView} ...` });
|
|
@@ -15,9 +16,9 @@ const TMDcmtTypeChooser = ({ tmSession, dataSource, disabled, backgroundColor, f
|
|
|
15
16
|
DcmtTypeListCacheService.GetFromTIDsAsync(values, false, tms).then(() => { TMSpinner.hide(); });
|
|
16
17
|
}, [values]);
|
|
17
18
|
const renderTemplate = () => {
|
|
18
|
-
return (_jsxs(StyledDivHorizontal, { style: { width: 'max-content', height: '100%' }, children: [values && _jsx(TMTidViewer, { tmSession: tmSession, tid: values[0], showIcon: true, showId: showId, noneSelectionText: placeHolder }), values && values.length > 1 && _jsx("p", { style: { marginLeft: '10px' }, children: `(+${values.length - 1} ${values.length == 2 ? 'altro' : 'altri'})` })] }));
|
|
19
|
+
return (_jsxs(StyledDivHorizontal, { style: { width: 'max-content', height: '100%' }, children: [values && _jsx(TMTidViewer, { tmSession: tmSession, tid: values[0], color: 'inherit', showIcon: true, showId: showId, noneSelectionText: placeHolder }), values && values.length > 1 && _jsx("p", { style: { margin: 0, marginLeft: '10px' }, children: `(+${values.length - 1} ${values.length == 2 ? 'altro' : 'altri'})` })] }));
|
|
19
20
|
};
|
|
20
|
-
return (_jsxs(_Fragment, { children: [_jsx(TMSummary, { backgroundColor: backgroundColor, buttons: buttons, showBorder: showBorder, borderRadius: borderRadius, hasValue: values && values.length > 0, showClearButton: showClearButton, showEditButton: showEditButton, iconEditButton: _jsx(IconSearch, { fontSize: 16 }), onEditorClick: () => {
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsx(TMSummary, { backgroundColor: backgroundColor, icon: icon, disabled: disabled, fontSize: fontSize, buttons: buttons, showBorder: showBorder, borderRadius: borderRadius, hasValue: values && values.length > 0, showClearButton: showClearButton, showEditButton: showEditButton, iconEditButton: _jsx(IconSearch, { fontSize: 16 }), onEditorClick: () => {
|
|
21
22
|
if (!disabled) {
|
|
22
23
|
setShowChooser(true);
|
|
23
24
|
updateIsModalOpen?.(true);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { LayoutModes } from '@topconsultnpm/sdk-ts';
|
|
3
|
+
import { TMDistinctValuesEventArgs } from '../../hooks/useTMDistinctValuesSelection';
|
|
3
4
|
interface ITMDistinctValues {
|
|
4
5
|
tid: number | undefined;
|
|
5
6
|
mid: number | undefined;
|
|
@@ -8,14 +9,15 @@ interface ITMDistinctValues {
|
|
|
8
9
|
allowAppendMode?: boolean;
|
|
9
10
|
showHeader?: boolean;
|
|
10
11
|
isModal?: boolean;
|
|
11
|
-
onSelectionChanged?: (e:
|
|
12
|
+
onSelectionChanged?: (e: TMDistinctValuesEventArgs) => void;
|
|
12
13
|
onClosePanelCallback?: () => void;
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Pannello da cui si preleva il valore di un campo del form scegliendolo fra quelli già archiviati.
|
|
17
|
+
*
|
|
18
|
+
* Le due sorgenti (valori distinti del metadato e ricerca rapida su una ricerca salvata) stanno in due hook
|
|
19
|
+
* dedicati che espongono la stessa interfaccia (righe, colonne, conteggi): qui restano la composizione,
|
|
20
|
+
* la scelta della sorgente da mostrare e il render.
|
|
21
|
+
*/
|
|
20
22
|
declare const TMDistinctValues: React.FC<ITMDistinctValues>;
|
|
21
23
|
export default TMDistinctValues;
|
|
@@ -1,209 +1,161 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef
|
|
3
|
-
import { LayoutModes,
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import { LayoutModes, SDK_Localizator } from '@topconsultnpm/sdk-ts';
|
|
4
4
|
import styled from 'styled-components';
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
5
|
+
import { calcResponsiveSizes, IconAll, IconDelete, IconPencil, IconRefresh, SDKUI_Localizator } from '../../helper';
|
|
6
|
+
import TMButton from '../base/TMButton';
|
|
7
|
+
import TMDataGrid from '../base/TMDataGrid';
|
|
8
|
+
import { CounterItemKey } from '../base/TMCounterContainer';
|
|
9
|
+
import { useDeviceType } from '../base/TMDeviceProvider';
|
|
9
10
|
import TMCheckBox from '../editors/TMCheckBox';
|
|
11
|
+
import TMDropDown from '../editors/TMDropDown';
|
|
10
12
|
import TMTextBox from '../editors/TMTextBox';
|
|
11
13
|
import TMPanel from '../base/TMPanel';
|
|
12
14
|
import TMModal from '../base/TMModal';
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import TMQuickSearchSettingsForm from './TMQuickSearchSettingsForm';
|
|
16
|
+
import TMSelectedValuesSummary from './TMSelectedValuesSummary';
|
|
17
|
+
import { useTMDistinctValuesMetadataDisplay } from '../../hooks/useTMDistinctValuesMetadataDisplay';
|
|
18
|
+
import { DistinctValuesModes, QUICK_SEARCH_VALUE_COLUMN_CLASS, useTMDistinctValuesQuickSearch } from '../../hooks/useTMDistinctValuesQuickSearch';
|
|
19
|
+
import { useTMDistinctValuesSelection } from '../../hooks/useTMDistinctValuesSelection';
|
|
20
|
+
import { useTMDistinctValuesSource } from '../../hooks/useTMDistinctValuesSource';
|
|
21
|
+
import { TMColors } from '../../utils/theme';
|
|
22
|
+
/* Il padding orizzontale stacca il contenuto dai bordi del pannello/modale che lo ospita */
|
|
23
|
+
const StyledDistinctValues = styled.div `display: flex; flex-direction: column; height: 100%; overflow: hidden; gap: 6px; padding: 4px 8px 6px; position: relative;`;
|
|
24
|
+
const StyledTopBar = styled.div `
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: row;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: flex-start;
|
|
29
|
+
gap: 15px;
|
|
30
|
+
flex-wrap: wrap;
|
|
31
|
+
padding: 6px 10px;
|
|
32
|
+
background-color: ${TMColors.toolbar_background};
|
|
33
|
+
border: 1px solid ${TMColors.border_normal};
|
|
34
|
+
border-radius: 6px;
|
|
35
|
+
`;
|
|
36
|
+
/* Accodamento e separatore stanno con il resto della configurazione: il divisore li distingue dai comandi senza staccarli dal gruppo */
|
|
37
|
+
const StyledAppendBar = styled.div `
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: row;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: 8px;
|
|
42
|
+
min-height: 30px;
|
|
43
|
+
padding-left: 10px;
|
|
44
|
+
border-left: 1px solid ${TMColors.border_normal};
|
|
45
|
+
`;
|
|
46
|
+
/* Modalità, comandi e opzioni di accodamento: tutta la configurazione del pannello su un'unica riga */
|
|
47
|
+
const StyledConfigBar = styled.div `display: flex; flex-direction: row; align-items: center; flex-wrap: wrap; gap: 4px;`;
|
|
48
|
+
const StyledGridContainer = styled.div `
|
|
49
|
+
flex: 1;
|
|
50
|
+
min-height: 0;
|
|
51
|
+
overflow: hidden;
|
|
52
|
+
|
|
53
|
+
/* Evidenzia intestazione e celle della colonna da cui viene prelevato il valore */
|
|
54
|
+
.${QUICK_SEARCH_VALUE_COLUMN_CLASS} {
|
|
55
|
+
background-color: ${TMColors.primary_container};
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
/* Quota di larghezza attribuita a ciascuna colonna della griglia, oltre a una base fissa per la barra dei comandi.
|
|
60
|
+
I limiti evitano i due estremi: sotto il 40% la barra dei comandi diventa illeggibile, oltre il 95% il modale esce dallo schermo */
|
|
61
|
+
const QUICK_SEARCH_BASE_WIDTH_PERC = 16;
|
|
62
|
+
const QUICK_SEARCH_COLUMN_WIDTH_PERC = 12;
|
|
63
|
+
const QUICK_SEARCH_MIN_WIDTH_PERC = 40;
|
|
64
|
+
const QUICK_SEARCH_MAX_WIDTH_PERC = 95;
|
|
65
|
+
/**
|
|
66
|
+
* Larghezza del modale della ricerca rapida, proporzionale al numero di colonne mostrate: con poche colonne
|
|
67
|
+
* un modale a tutta pagina le lascerebbe stirate e meno leggibili, con molte serve tutto lo spazio disponibile.
|
|
68
|
+
*/
|
|
69
|
+
const calcQuickSearchModalWidth = (visibleColumnsCount) => {
|
|
70
|
+
const widthPerc = QUICK_SEARCH_BASE_WIDTH_PERC + visibleColumnsCount * QUICK_SEARCH_COLUMN_WIDTH_PERC;
|
|
71
|
+
return `${Math.min(QUICK_SEARCH_MAX_WIDTH_PERC, Math.max(QUICK_SEARCH_MIN_WIDTH_PERC, widthPerc))}%`;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Pannello da cui si preleva il valore di un campo del form scegliendolo fra quelli già archiviati.
|
|
75
|
+
*
|
|
76
|
+
* Le due sorgenti (valori distinti del metadato e ricerca rapida su una ricerca salvata) stanno in due hook
|
|
77
|
+
* dedicati che espongono la stessa interfaccia (righe, colonne, conteggi): qui restano la composizione,
|
|
78
|
+
* la scelta della sorgente da mostrare e il render.
|
|
79
|
+
*/
|
|
17
80
|
const TMDistinctValues = ({ tid, mid, layoutMode = LayoutModes.None, allowAppendMode = true, showHeader = true, isModal, separator = ",", onSelectionChanged, onClosePanelCallback }) => {
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}, [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
const permissionCheck = {
|
|
43
|
-
[LayoutModes.Ark]: md.perm?.canArchive,
|
|
44
|
-
[LayoutModes.Update]: md.perm?.canUpdate,
|
|
45
|
-
[LayoutModes.None]: md.perm?.canSearch,
|
|
46
|
-
};
|
|
47
|
-
if (layoutMode in permissionCheck && permissionCheck[layoutMode] !== AccessLevels.Yes)
|
|
48
|
-
return;
|
|
49
|
-
if (stringIsNullOrEmpty(focusedItem.value)) {
|
|
50
|
-
setAppendedValues(new Set());
|
|
51
|
-
onSelectionChanged?.({ tid: tid, mid: mid, newValue: undefined, isAppendMode: isAppendMode });
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
let value = md.dataType === MetadataDataTypes.Number ? parseFloat(focusedItem.value.replace(',', '.')).toString() : focusedItem.value;
|
|
55
|
-
// Fuori dalla modalità Accoda la selezione sostituisce sempre il valore precedente
|
|
56
|
-
const newAppendedValues = isAppendMode ? new Set(appendedValues) : new Set();
|
|
57
|
-
newAppendedValues.add(value);
|
|
58
|
-
setAppendedValues(newAppendedValues);
|
|
59
|
-
onSelectionChanged?.({ tid: tid, mid: mid, newValue: joinAppendedValues(newAppendedValues), isAppendMode: isAppendMode });
|
|
60
|
-
}, [focusedItem]);
|
|
61
|
-
const joinAppendedValues = (values) => Array.from(values).join(separator);
|
|
62
|
-
const onAppendModeChanged = () => {
|
|
63
|
-
const newIsAppendMode = !isAppendMode;
|
|
64
|
-
// Disattivando l'accodamento si riparte da zero: il prossimo click sostituirà il valore
|
|
65
|
-
if (!newIsAppendMode)
|
|
66
|
-
setAppendedValues(new Set());
|
|
67
|
-
setIsAppendMode(newIsAppendMode);
|
|
68
|
-
};
|
|
69
|
-
const getDistictValuesAsync = async () => {
|
|
70
|
-
if (!tid)
|
|
81
|
+
const deviceType = useDeviceType();
|
|
82
|
+
const display = useTMDistinctValuesMetadataDisplay();
|
|
83
|
+
const distinctValues = useTMDistinctValuesSource({ tid, mid, display });
|
|
84
|
+
const quickSearch = useTMDistinctValuesQuickSearch({ tid, mid, md: distinctValues.md, display });
|
|
85
|
+
const { isQuickSearchMode, isQuickSearchGridVisible } = quickSearch;
|
|
86
|
+
// Il valore della riga sta in campi diversi nelle due sorgenti: nei valori distinti è sempre `value`,
|
|
87
|
+
// nella ricerca rapida è la colonna del metadato configurato
|
|
88
|
+
const getRowValue = useCallback((row) => {
|
|
89
|
+
if (!isQuickSearchGridVisible)
|
|
90
|
+
return row?.value;
|
|
91
|
+
return quickSearch.valueField ? row?.[quickSearch.valueField] : undefined;
|
|
92
|
+
}, [isQuickSearchGridVisible, quickSearch.valueField]);
|
|
93
|
+
const selection = useTMDistinctValuesSelection({
|
|
94
|
+
tid, mid, md: distinctValues.md, layoutMode, allowAppendMode, separator,
|
|
95
|
+
selectedValueMd: quickSearch.selectedValueMd,
|
|
96
|
+
getRowValue,
|
|
97
|
+
getMetadataDisplayValue: display.getMetadataDisplayValue,
|
|
98
|
+
onSelectionChanged
|
|
99
|
+
});
|
|
100
|
+
// Cambiando sorgente la riga focalizzata non esiste più: la griglia viene ricreata da zero
|
|
101
|
+
useEffect(() => { selection.setFocusedItem(undefined); }, [quickSearch.mode, quickSearch.settings]);
|
|
102
|
+
const refreshAsync = async () => {
|
|
103
|
+
if (!isQuickSearchMode) {
|
|
104
|
+
await distinctValues.loadAsync();
|
|
71
105
|
return;
|
|
72
|
-
const requestId = ++requestIdRef.current;
|
|
73
|
-
// Svuota subito la griglia: evita di mostrare i valori del tid/mid precedente durante il caricamento
|
|
74
|
-
setDataSource([]);
|
|
75
|
-
try {
|
|
76
|
-
TMSpinner.show({ description: 'Caricamento dei valori distinti...' });
|
|
77
|
-
let dtd = await DcmtTypeListCacheService.GetAsync(tid, true);
|
|
78
|
-
const currentMd = dtd?.metadata?.find(o => o.id === mid);
|
|
79
|
-
let result = await SDK_Globals.tmSession?.NewSearchEngine().GetDistinctValuesAsync(tid, mid, 10000);
|
|
80
|
-
if (requestId !== requestIdRef.current)
|
|
81
|
-
return;
|
|
82
|
-
// Load DataList or UserID cache based on metadata domain
|
|
83
|
-
if (currentMd?.dataDomain === MetadataDataDomains.DataList && currentMd.dataListID) {
|
|
84
|
-
await loadDataListsAsync(new Set([currentMd.dataListID]));
|
|
85
|
-
}
|
|
86
|
-
if (currentMd?.dataDomain === MetadataDataDomains.UserID && result?.dtdResult?.rows) {
|
|
87
|
-
const userIDs = new Set();
|
|
88
|
-
result.dtdResult.rows.forEach((row) => {
|
|
89
|
-
const userId = Number(row[0]);
|
|
90
|
-
if (userId && userId > 0) {
|
|
91
|
-
userIDs.add(userId);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
await loadUsersAsync(userIDs);
|
|
95
|
-
}
|
|
96
|
-
// Nel frattempo tid/mid sono cambiati: questa risposta (e le cache appena caricate) non è più valida
|
|
97
|
-
if (requestId !== requestIdRef.current)
|
|
98
|
-
return;
|
|
99
|
-
// md e dataSource vengono impostati insieme, dopo il popolamento delle cache,
|
|
100
|
-
// così il render delle celle usa sempre metadato e cache coerenti con i dati
|
|
101
|
-
setMd(currentMd);
|
|
102
|
-
setDataSource(convertDataTableToObject(result?.dtdResult));
|
|
103
|
-
}
|
|
104
|
-
catch (e) {
|
|
105
|
-
if (requestId !== requestIdRef.current)
|
|
106
|
-
return;
|
|
107
|
-
let err = e;
|
|
108
|
-
TMExceptionBoxManager.show({ exception: err });
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
TMSpinner.hide();
|
|
112
106
|
}
|
|
107
|
+
await quickSearch.refreshAsync();
|
|
113
108
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}, [md, valueCellRender, calculateValueDisplayValue]);
|
|
154
|
-
const customSummary = useMemo(() => {
|
|
155
|
-
return ({
|
|
156
|
-
totalItems: [
|
|
157
|
-
{ column: 'value', summaryType: 'count' },
|
|
158
|
-
{ column: 'Count', summaryType: 'sum' }
|
|
159
|
-
]
|
|
160
|
-
});
|
|
161
|
-
}, []);
|
|
162
|
-
const convertDataTableToObject = (dtdResult) => {
|
|
163
|
-
if (!dtdResult?.columns?.length || !dtdResult?.rows?.length)
|
|
164
|
-
return [];
|
|
165
|
-
// Descrittori di colonna calcolati una sola volta invece che per ogni riga
|
|
166
|
-
const columns = [];
|
|
167
|
-
dtdResult.columns.forEach((col, colIndex) => {
|
|
168
|
-
if (col?.caption) {
|
|
169
|
-
columns.push({ index: colIndex, key: colIndex === 0 ? "value" : col.caption, isNumber: col.dataType === DataColumnTypes.Number });
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
// Il conteggio delle occorrenze è la seconda colonna del risultato:
|
|
173
|
-
// la chiave di ordinamento viene calcolata una volta per riga, non a ogni confronto
|
|
174
|
-
const COUNT_COL_INDEX = 1;
|
|
175
|
-
const rows = dtdResult.rows.map((row) => {
|
|
176
|
-
let rowObject = { rowIndex: 0, sortValue: 0 };
|
|
177
|
-
for (let i = 0; i < columns.length; i++) {
|
|
178
|
-
const col = columns[i];
|
|
179
|
-
rowObject[col.key] = col.isNumber ? row?.[col.index]?.replace('.', ',') : row?.[col.index] ?? null;
|
|
180
|
-
}
|
|
181
|
-
rowObject.sortValue = Number(row?.[COUNT_COL_INDEX]) || 0;
|
|
182
|
-
return rowObject;
|
|
183
|
-
});
|
|
184
|
-
rows.sort((a, b) => b.sortValue - a.sortValue);
|
|
185
|
-
for (let i = 0; i < rows.length; i++)
|
|
186
|
-
rows[i].rowIndex = i;
|
|
187
|
-
return rows;
|
|
188
|
-
};
|
|
189
|
-
const isVisibleAppend = () => {
|
|
190
|
-
if (!allowAppendMode)
|
|
191
|
-
return false;
|
|
192
|
-
if (layoutMode !== LayoutModes.Ark && layoutMode !== LayoutModes.Update)
|
|
193
|
-
return true;
|
|
194
|
-
if (!md)
|
|
195
|
-
return true;
|
|
196
|
-
if (md.dataType === MetadataDataTypes.DateTime)
|
|
197
|
-
return false;
|
|
198
|
-
if (md.dataType === MetadataDataTypes.Number)
|
|
199
|
-
return false;
|
|
200
|
-
return md.dataDomain === undefined || md.dataDomain === MetadataDataDomains.None;
|
|
201
|
-
};
|
|
109
|
+
// Finché le colonne della ricerca rapida non sono pronte la griglia non va montata: nascerebbe senza campi
|
|
110
|
+
const isGridReady = !isQuickSearchGridVisible || quickSearch.columns.length > 0;
|
|
111
|
+
const counterConfig = useMemo(() => {
|
|
112
|
+
const counts = isQuickSearchGridVisible ? quickSearch.counts : distinctValues.counts;
|
|
113
|
+
// Senza il totale resta il conteggio predefinito della griglia: un rapporto con totale a zero sarebbe fuorviante
|
|
114
|
+
if (!counts?.found)
|
|
115
|
+
return { show: true };
|
|
116
|
+
return {
|
|
117
|
+
show: true,
|
|
118
|
+
items: new Map([
|
|
119
|
+
[CounterItemKey.allItems, {
|
|
120
|
+
key: CounterItemKey.allItems,
|
|
121
|
+
show: true,
|
|
122
|
+
caption: `${counts.returned} restituiti su ${counts.found} trovati`,
|
|
123
|
+
value: `${counts.returned}/${counts.found}`,
|
|
124
|
+
icon: _jsx(IconAll, {}),
|
|
125
|
+
order: -1
|
|
126
|
+
}]
|
|
127
|
+
])
|
|
128
|
+
};
|
|
129
|
+
}, [isQuickSearchGridVisible, quickSearch.counts, distinctValues.counts]);
|
|
130
|
+
const modeDataSource = useMemo(() => ([
|
|
131
|
+
{ value: DistinctValuesModes.DistinctValues, display: SDKUI_Localizator.DistinctValues },
|
|
132
|
+
{ value: DistinctValuesModes.QuickSearch, display: SDK_Localizator.SavedQuery },
|
|
133
|
+
]), []);
|
|
134
|
+
const panelTitle = (isQuickSearchGridVisible ? SDK_Localizator.SavedQuery : SDKUI_Localizator.DistinctValues) + (distinctValues.md?.nameLoc ? ` (${distinctValues.md?.nameLoc})` : '');
|
|
135
|
+
/* Le colonne nascoste (metadati di sistema della query) non occupano spazio: non vanno contate nella larghezza.
|
|
136
|
+
Durante un ricaricamento le colonne tornano momentaneamente vuote: si conserva l'ultima larghezza calcolata,
|
|
137
|
+
altrimenti il modale si restringerebbe per poi riallargarsi */
|
|
138
|
+
const lastQuickSearchModalWidthRef = useRef();
|
|
139
|
+
const quickSearchModalWidth = useMemo(() => {
|
|
140
|
+
const visibleColumnsCount = quickSearch.columns.filter(col => col.visible !== false).length;
|
|
141
|
+
if (visibleColumnsCount > 0)
|
|
142
|
+
lastQuickSearchModalWidthRef.current = calcQuickSearchModalWidth(visibleColumnsCount);
|
|
143
|
+
return lastQuickSearchModalWidthRef.current;
|
|
144
|
+
}, [quickSearch.columns]);
|
|
145
|
+
/* La larghezza è nota solo a ricerca conclusa: mostrare subito il modale lo farebbe nascere alla larghezza minima
|
|
146
|
+
per poi allargarlo sotto gli occhi dell'utente, quindi si attende (l'attesa è coperta dallo spinner della ricerca) */
|
|
147
|
+
const isModalReady = !isQuickSearchGridVisible || !!quickSearchModalWidth || !quickSearch.isFirstSearchPending;
|
|
202
148
|
const renderContent = () => {
|
|
203
|
-
return (_jsxs(StyledDistinctValues, { children: [
|
|
149
|
+
return (_jsxs(StyledDistinctValues, { children: [_jsx(StyledTopBar, { children: _jsxs(StyledConfigBar, { children: [_jsx(TMDropDown, { width: '180px', value: quickSearch.mode, dataSource: modeDataSource, elementStyle: { display: 'flex', alignItems: 'center' }, onValueChanged: (e) => quickSearch.onModeChanged(Number(e.target.value)) }), isQuickSearchMode &&
|
|
150
|
+
_jsx(TMButton, { btnStyle: 'toolbar', caption: SDKUI_Localizator.Settings, icon: _jsx(IconPencil, {}), onClick: () => quickSearch.setShowSettingsForm(true) }), isQuickSearchMode && Boolean(quickSearch.settings) &&
|
|
151
|
+
_jsx(TMButton, { btnStyle: 'toolbar', color: 'error', caption: SDKUI_Localizator.Delete, icon: _jsx(IconDelete, {}), onClick: quickSearch.onDeleteSettingsClick }), _jsx(TMButton, { btnStyle: 'toolbar', caption: SDKUI_Localizator.Refresh, icon: _jsx(IconRefresh, {}), onClick: () => refreshAsync() }), selection.isAppendVisible && _jsxs(StyledAppendBar, { children: [_jsx(TMCheckBox, { label: 'Accoda e separa con', value: selection.isAppendMode, onValueChanged: selection.onAppendModeChanged }), _jsx(TMTextBox, { width: '30px', readOnly: true, disabled: !selection.isAppendMode, value: separator })] })] }) }), _jsx(TMSelectedValuesSummary, { items: selection.selectedValues, onRemove: selection.onRemoveSelectedValue, onClear: selection.onClearSelectedValues }), _jsx(StyledGridContainer, { children: isGridReady && _jsx(TMDataGrid
|
|
152
|
+
/* Le due modalità hanno colonne del tutto diverse: ognuna ha la propria istanza di griglia.
|
|
153
|
+
Nei valori distinti l'intestazione è il nome del metadato, che arriva insieme al primo caricamento */
|
|
154
|
+
, { focusedRowKey: selection.focusedItem ? selection.focusedItem.rowIndex : undefined, selection: { showCheckBoxesMode: 'none' }, searchPanel: { highlightCaseSensitive: true, visible: true }, dataColumns: isQuickSearchGridVisible ? quickSearch.columns : distinctValues.columns, dataSource: isQuickSearchGridVisible ? quickSearch.dataSource : distinctValues.dataSource, customizeColumns: isQuickSearchGridVisible ? quickSearch.customizeColumns : distinctValues.customizeColumns, keyExpr: 'rowIndex', height: 'calc(100%)', showHeaderColumnChooser: true, onFocusedRowChanged: selection.onFocusedRowChanged, onRowDblClick: () => onClosePanelCallback?.(), counterConfig: counterConfig }, isQuickSearchGridVisible ? 'quickSearch' : `distinctValues_${distinctValues.md?.id ?? 0}`) })] }));
|
|
204
155
|
};
|
|
205
|
-
return (
|
|
206
|
-
|
|
207
|
-
|
|
156
|
+
return (_jsxs(_Fragment, { children: [isModal
|
|
157
|
+
? isModalReady && _jsx(TMModal, { title: panelTitle, width: isQuickSearchGridVisible ? calcResponsiveSizes(deviceType, quickSearchModalWidth ?? calcQuickSearchModalWidth(0), '95%', '95%') : calcResponsiveSizes(deviceType, '700px', '95%', '95%'), height: isQuickSearchGridVisible ? '95%' : calcResponsiveSizes(deviceType, '600px', '95%', '95%'), resizable: true, expandable: true, onClose: onClosePanelCallback, children: renderContent() }, isQuickSearchGridVisible ? 'quickSearch' : 'distinctValues')
|
|
158
|
+
: _jsx(TMPanel, { title: panelTitle, showHeader: showHeader, onClose: onClosePanelCallback, children: renderContent() }), quickSearch.showSettingsForm &&
|
|
159
|
+
_jsx(TMQuickSearchSettingsForm, { settings: quickSearch.settings, onSave: quickSearch.onSaveSettings, onDelete: quickSearch.onDeleteSettingsClick, onClose: quickSearch.onSettingsFormClose }), _jsx(quickSearch.ConfirmQueryParamsDialog, {})] }));
|
|
208
160
|
};
|
|
209
161
|
export default TMDistinctValues;
|
|
@@ -21,7 +21,7 @@ const TMMetadataChooser = ({ tmSession, dataSource, showEditButton = true, butto
|
|
|
21
21
|
return undefined;
|
|
22
22
|
};
|
|
23
23
|
const renderTemplate = useMemo(() => {
|
|
24
|
-
return (_jsxs(StyledDivHorizontal, { style: { width: 'max-content', height: '100%' }, children: [values && values.length > 0 && values[0].mid && values[0].mid > 0 && _jsx(TMMidViewer, { tmSession: tmSession, tid_mid: values[0], showIcon: true, showId: showId, showCompleteName: showCompleteMetadataName }), values && values.length > 0 && values[0].mid && values[0].mid < 0 && _jsx(TMMidViewer, { tmSession: tmSession, tid_mid: values[0], inputMd: getinputMd(), showIcon: true, showId: showId, showCompleteName: showCompleteMetadataName }), values && values.length > 1 && _jsx("p", { style: { marginLeft: '10px' }, children: `(+${values.length - 1} ${values.length == 2 ? 'altro' : 'altri'})` }), (values == undefined || values.length == 0) && _jsx("p", { children: placeHolder ?? SDKUI_Localizator.SelectMetadata })] }));
|
|
24
|
+
return (_jsxs(StyledDivHorizontal, { style: { width: 'max-content', height: '100%' }, children: [values && values.length > 0 && values[0].mid && values[0].mid > 0 && _jsx(TMMidViewer, { tmSession: tmSession, tid_mid: values[0], color: 'inherit', showIcon: true, showId: showId, showCompleteName: showCompleteMetadataName }), values && values.length > 0 && values[0].mid && values[0].mid < 0 && _jsx(TMMidViewer, { tmSession: tmSession, tid_mid: values[0], inputMd: getinputMd(), color: 'inherit', showIcon: true, showId: showId, showCompleteName: showCompleteMetadataName }), values && values.length > 1 && _jsx("p", { style: { margin: 0, marginLeft: '10px' }, children: `(+${values.length - 1} ${values.length == 2 ? 'altro' : 'altri'})` }), (values == undefined || values.length == 0) && _jsx("p", { style: { margin: 0 }, children: placeHolder ?? SDKUI_Localizator.SelectMetadata })] }));
|
|
25
25
|
}, [values, tmSession, showId, showCompleteMetadataName, placeHolder]);
|
|
26
26
|
return (_jsxs(_Fragment, { children: [_jsx(TMSummary, { label: label, width: width, height: height, disabled: disabled, validationItems: validationItems, backgroundColor: backgroundColor, buttons: buttons, placeHolder: placeHolder, fontSize: fontSize, showBorder: showBorder, borderRadius: borderRadius, hasValue: values && values.length > 0, showClearButton: showClearButton, showEditButton: showEditButton, iconEditButton: _jsx(IconSearch, { fontSize: 16 }), openEditorOnSummaryClick: openEditorOnSummaryClick, onEditorClick: () => {
|
|
27
27
|
if (!disabled) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TMEditorButtonData } from '../base/TMEditorBase';
|
|
2
|
+
interface ITMQuickSearchInfo {
|
|
3
|
+
/** Tipo documento della ricerca rapida: serve a ricostruire la ricerca di sistema con tutti i documenti */
|
|
4
|
+
tid: number | undefined;
|
|
5
|
+
/** Senza ricerca salvata non c'è nulla da spiegare e l'icona non viene mostrata */
|
|
6
|
+
sqdId: number | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare const useTMQuickSearchInfo: ({ tid, sqdId }: ITMQuickSearchInfo) => {
|
|
9
|
+
infoButton: TMEditorButtonData | undefined;
|
|
10
|
+
detailForm: import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
};
|
|
12
|
+
export {};
|