@topconsultnpm/sdkui-react 6.22.0-dev2.2 → 6.22.0-dev2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
  2. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
  3. package/lib/components/base/TMButton.d.ts +13 -0
  4. package/lib/components/base/TMButton.js +137 -5
  5. package/lib/components/base/TMDataGrid.d.ts +2 -0
  6. package/lib/components/base/TMDataGrid.js +68 -4
  7. package/lib/components/base/TMEditorBase.d.ts +2 -0
  8. package/lib/components/base/TMModal.d.ts +4 -0
  9. package/lib/components/base/TMModal.js +42 -11
  10. package/lib/components/base/TMPanel.d.ts +1 -0
  11. package/lib/components/base/TMPanel.js +79 -20
  12. package/lib/components/base/TMTooltip.d.ts +2 -1
  13. package/lib/components/base/TMTooltip.js +23 -6
  14. package/lib/components/base/TMTreeView.js +108 -67
  15. package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
  16. package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
  17. package/lib/components/choosers/TMDistinctValues.js +149 -142
  18. package/lib/components/choosers/TMMetadataChooser.js +1 -1
  19. package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
  20. package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
  21. package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
  22. package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
  23. package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
  24. package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
  25. package/lib/components/editors/TMDropDown.d.ts +3 -0
  26. package/lib/components/editors/TMDropDown.js +197 -5
  27. package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
  28. package/lib/components/editors/TMMetadataEditor.js +8 -3
  29. package/lib/components/editors/TMTextBox.js +15 -15
  30. package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
  31. package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
  32. package/lib/components/features/documents/TMDcmtForm.js +104 -42
  33. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
  34. package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
  35. package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
  36. package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
  37. package/lib/components/features/documents/TMMasterInfoFields.js +172 -0
  38. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
  39. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +256 -0
  40. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +352 -0
  41. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +221 -0
  42. package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
  43. package/lib/components/features/documents/TMRelationViewer.js +69 -20
  44. package/lib/components/features/search/TMSearch.js +29 -16
  45. package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
  46. package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
  47. package/lib/components/features/search/TMSearchQueryPanel.js +28 -7
  48. package/lib/components/features/search/TMSearchResult.d.ts +9 -0
  49. package/lib/components/features/search/TMSearchResult.js +323 -92
  50. package/lib/components/features/search/TMTreeSelector.js +18 -5
  51. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
  52. package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
  53. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  54. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
  55. package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
  56. package/lib/components/layout/panelManager/types.d.ts +3 -0
  57. package/lib/components/viewers/TMMidViewer.js +4 -1
  58. package/lib/components/viewers/TMTidViewer.js +25 -11
  59. package/lib/helper/SDKUI_Globals.d.ts +55 -0
  60. package/lib/helper/SDKUI_Globals.js +82 -0
  61. package/lib/helper/SDKUI_Localizator.d.ts +23 -0
  62. package/lib/helper/SDKUI_Localizator.js +230 -0
  63. package/lib/helper/TMIcons.d.ts +1 -0
  64. package/lib/helper/TMIcons.js +3 -0
  65. package/lib/helper/dataGridSearchHelper.d.ts +28 -0
  66. package/lib/helper/dataGridSearchHelper.js +51 -0
  67. package/lib/helper/dcmtsHelper.d.ts +20 -0
  68. package/lib/helper/dcmtsHelper.js +58 -1
  69. package/lib/helper/helpers.d.ts +20 -0
  70. package/lib/helper/helpers.js +40 -0
  71. package/lib/helper/index.d.ts +1 -0
  72. package/lib/helper/index.js +1 -0
  73. package/lib/helper/queryHelper.d.ts +2 -0
  74. package/lib/helper/queryHelper.js +3 -1
  75. package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
  76. package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
  77. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  78. package/lib/hooks/useArchiveListForm.js +46 -0
  79. package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
  80. package/lib/hooks/useCaseFlowApprove.js +157 -0
  81. package/lib/hooks/useDataUserIdItem.d.ts +1 -0
  82. package/lib/hooks/useDataUserIdItem.js +1 -0
  83. package/lib/hooks/useDocumentOperations.d.ts +2 -0
  84. package/lib/hooks/useDocumentOperations.js +28 -5
  85. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +97 -0
  86. package/lib/hooks/useMultiMasterDetailDcmts.js +555 -0
  87. package/lib/hooks/useRelatedDocuments.js +2 -26
  88. package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
  89. package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
  90. package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
  91. package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
  92. package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
  93. package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
  94. package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
  95. package/lib/hooks/useTMDistinctValuesSource.js +105 -0
  96. package/lib/index.d.ts +1 -0
  97. package/lib/index.js +1 -0
  98. package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
  99. package/lib/services/caseflow/CaseFlowService.js +693 -0
  100. package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
  101. package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
  102. package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
  103. package/lib/services/caseflow/caseFlowNameCache.js +116 -0
  104. package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
  105. package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
  106. package/lib/services/index.d.ts +4 -0
  107. package/lib/services/index.js +4 -0
  108. package/lib/services/platform_services.d.ts +1 -1
  109. package/package.json +66 -66
@@ -1,154 +1,161 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useState } from 'react';
3
- import { LayoutModes, MetadataDataDomains, AccessLevels, MetadataDataTypes, SDK_Globals, DcmtTypeListCacheService, DataColumnTypes, DataListViewModes } from '@topconsultnpm/sdk-ts';
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 { SDKUI_Localizator, stringIsNullOrEmpty } from '../../helper';
6
- import TMDataGrid, { TMDataGridPageSize } from '../base/TMDataGrid';
7
- import { TMExceptionBoxManager } from '../base/TMPopUp';
8
- import TMSpinner from '../base/TMSpinner';
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';
12
+ import TMTextBox from '../editors/TMTextBox';
10
13
  import TMPanel from '../base/TMPanel';
11
14
  import TMModal from '../base/TMModal';
12
- import { useDataListItem } from '../../hooks/useDataListItem';
13
- import { useDataUserIdItem } from '../../hooks/useDataUserIdItem';
14
- const StyledDistinctValues = styled.div `display: flex; flex-direction: column; height: 100%; overflow: hidden; gap: 10px; position: relative;`;
15
- const TMDistinctValues = ({ tid, mid, layoutMode = LayoutModes.None, allowAppendMode = true, showHeader = true, isModal, separator = " ", onSelectionChanged, onClosePanelCallback }) => {
16
- const [focusedItem, setFocusedItem] = useState();
17
- const [dataSource, setDataSource] = useState([]);
18
- const [isAppendMode, setIsAppendMode] = useState(false);
19
- const [md, setMd] = useState();
20
- const [currentValue, setCurrentValue] = useState('');
21
- const { loadDataListsAsync, renderDataListCell, dataListsCache } = useDataListItem();
22
- const { loadUsersAsync, renderUserIdViewer, usersCache } = useDataUserIdItem();
23
- useEffect(() => {
24
- getDistictValuesAsync();
25
- }, [mid]);
26
- useEffect(() => {
27
- setIsAppendMode(false);
28
- setFocusedItem(undefined);
29
- setCurrentValue('');
30
- }, [md]);
31
- useEffect(() => {
32
- if (!focusedItem)
33
- return;
34
- if (!md)
35
- return;
36
- if ((layoutMode === LayoutModes.Ark || layoutMode === LayoutModes.Update) && md.dataDomain === MetadataDataDomains.Computed)
37
- return;
38
- const permissionCheck = {
39
- [LayoutModes.Ark]: md.perm?.canArchive,
40
- [LayoutModes.Update]: md.perm?.canUpdate,
41
- [LayoutModes.None]: md.perm?.canSearch,
42
- };
43
- if (layoutMode in permissionCheck && permissionCheck[layoutMode] !== AccessLevels.Yes)
44
- return;
45
- if (!focusedItem.value) {
46
- setCurrentValue('');
47
- onSelectionChanged?.({ tid: tid, mid: mid, newValue: undefined, isAppendMode: isAppendMode });
48
- return;
49
- }
50
- let value = md.dataType === MetadataDataTypes.Number ? parseFloat(focusedItem.value.replace(',', '.')).toString() : focusedItem.value;
51
- let newValue = stringIsNullOrEmpty(currentValue)
52
- ? value
53
- : isAppendMode ? currentValue + separator + value : value;
54
- setCurrentValue(newValue);
55
- onSelectionChanged?.({ tid: tid, mid: mid, newValue: newValue, isAppendMode: isAppendMode });
56
- }, [focusedItem]);
57
- const getDistictValuesAsync = async () => {
58
- if (!tid)
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
+ */
80
+ const TMDistinctValues = ({ tid, mid, layoutMode = LayoutModes.None, allowAppendMode = true, showHeader = true, isModal, separator = ",", onSelectionChanged, onClosePanelCallback }) => {
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();
59
105
  return;
60
- try {
61
- TMSpinner.show({ description: 'Caricamento dei valori distinti...' });
62
- let dtd = await DcmtTypeListCacheService.GetAsync(tid, true);
63
- const currentMd = dtd?.metadata?.find(o => o.id === mid);
64
- setMd(currentMd);
65
- let result = await SDK_Globals.tmSession?.NewSearchEngine().GetDistinctValuesAsync(tid, mid, 10000);
66
- // Load DataList or UserID cache based on metadata domain
67
- if (currentMd?.dataDomain === MetadataDataDomains.DataList && currentMd.dataListID) {
68
- await loadDataListsAsync(new Set([currentMd.dataListID]));
69
- }
70
- if (currentMd?.dataDomain === MetadataDataDomains.UserID && result?.dtdResult?.rows) {
71
- const userIDs = new Set();
72
- result.dtdResult.rows.forEach((row) => {
73
- const userId = Number(row[0]);
74
- if (userId && userId > 0) {
75
- userIDs.add(userId);
76
- }
77
- });
78
- await loadUsersAsync(userIDs);
79
- }
80
- setDataSource(convertDataTableToObject(result?.dtdResult));
81
- }
82
- catch (e) {
83
- let err = e;
84
- TMExceptionBoxManager.show({ exception: err });
85
- }
86
- finally {
87
- TMSpinner.hide();
88
106
  }
107
+ await quickSearch.refreshAsync();
89
108
  };
90
- const onFocusedRowChanged = useCallback((e) => {
91
- setFocusedItem(e.row?.data);
92
- }, []);
93
- const valueCellRender = useCallback((cellData) => {
94
- if (!cellData || cellData.value === undefined)
95
- return null;
96
- if (md?.dataDomain === MetadataDataDomains.DataList && md.dataListID) {
97
- return renderDataListCell(cellData.value, md.dataListID, md.dataListViewMode ?? DataListViewModes.None);
98
- }
99
- if (md?.dataDomain === MetadataDataDomains.UserID) {
100
- return renderUserIdViewer(Number(cellData.value), true);
101
- }
102
- return _jsx("div", { children: cellData.text });
103
- }, [md, renderDataListCell, renderUserIdViewer]);
104
- const customColumns = useMemo(() => {
105
- const needsCellRender = md?.dataDomain === MetadataDataDomains.DataList || md?.dataDomain === MetadataDataDomains.UserID;
106
- return ([
107
- { dataField: 'value', dataType: md?.dataType === MetadataDataTypes.DateTime ? 'date' : 'string', caption: md?.nameLoc, width: 'min-content', ...(needsCellRender && { cellRender: valueCellRender }) },
108
- { dataField: 'Count', dataType: 'number', caption: 'N°' }
109
- ]);
110
- }, [md, valueCellRender]);
111
- const customSummary = useMemo(() => {
112
- return ({
113
- totalItems: [
114
- { column: 'value', summaryType: 'count' },
115
- { column: 'Count', summaryType: 'sum' }
116
- ]
117
- });
118
- }, []);
119
- const convertDataTableToObject = (dtdResult) => {
120
- if (!dtdResult?.columns?.length || !dtdResult?.rows?.length)
121
- return [];
122
- return dtdResult.rows.map((row, index) => {
123
- let rowObject = { rowIndex: index };
124
- dtdResult.columns && dtdResult.columns.forEach((col, colIndex) => {
125
- if (col?.caption) {
126
- rowObject[colIndex === 0 ? "value" : col.caption] = col.dataType === DataColumnTypes.Number
127
- ? row?.[colIndex]?.replace('.', ',')
128
- : row?.[colIndex] ?? null;
129
- }
130
- });
131
- return rowObject;
132
- });
133
- };
134
- const isVisibleAppend = () => {
135
- if (!allowAppendMode)
136
- return false;
137
- if (layoutMode !== LayoutModes.Ark && layoutMode !== LayoutModes.Update)
138
- return true;
139
- if (!md)
140
- return true;
141
- if (md.dataType === MetadataDataTypes.DateTime)
142
- return false;
143
- if (md.dataType === MetadataDataTypes.Number)
144
- return false;
145
- return md.dataDomain === undefined || md.dataDomain === MetadataDataDomains.None;
146
- };
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;
147
148
  const renderContent = () => {
148
- return (_jsxs(StyledDistinctValues, { children: [isVisibleAppend() && _jsx(TMCheckBox, { elementStyle: { position: 'absolute', right: '15px', top: '15px', zIndex: 10000 }, label: 'Accoda', value: isAppendMode, onValueChanged: () => { setIsAppendMode(!isAppendMode); } }), _jsx(TMDataGrid, { focusedRowKey: focusedItem ? focusedItem.rowIndex : undefined, selection: { showCheckBoxesMode: 'none' }, searchPanel: { highlightCaseSensitive: true, visible: true }, dataColumns: customColumns, dataSource: dataSource, keyExpr: 'rowIndex', height: 'calc(100%)', onFocusedRowChanged: onFocusedRowChanged, paging: { pageSize: 30 }, summary: customSummary, onRowDblClick: () => !isAppendMode && onClosePanelCallback?.(), pageSize: TMDataGridPageSize.Medium })] }));
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}`) })] }));
149
155
  };
150
- return (_jsx(_Fragment, { children: isModal
151
- ? _jsx(TMModal, { title: SDKUI_Localizator.DistinctValues + (md?.nameLoc ? ` (${md?.nameLoc})` : ''), height: '600px', width: '500px', resizable: true, onClose: onClosePanelCallback, children: renderContent() })
152
- : _jsx(TMPanel, { title: SDKUI_Localizator.DistinctValues + (md?.nameLoc ? ` (${md?.nameLoc})` : ''), showHeader: showHeader, onClose: onClosePanelCallback, children: renderContent() }) }));
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, {})] }));
153
160
  };
154
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 {};
@@ -0,0 +1,279 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { DcmtTypeListCacheService, PlatformObjectValidator, SavedQueryCacheService, SDK_Globals, SDK_Localizator, SearchEngine } from '@topconsultnpm/sdk-ts';
4
+ import styled from 'styled-components';
5
+ import { displayMetadataValue, getSysAllDcmtsSQD, IconInfo, IconSavedQuery, LocalizeQueryOperators, SDKUI_Localizator, SYS_ALL_DCMTS_SQD_ID } from '../../helper';
6
+ import { needsDisplayValue, useTMDistinctValuesMetadataDisplay } from '../../hooks/useTMDistinctValuesMetadataDisplay';
7
+ import TMModal from '../base/TMModal';
8
+ import { TMColors } from '../../utils/theme';
9
+ const TOOLTIP_MAX_CONDITIONS = 8;
10
+ const QUERY_PARAM_PREFIX = '{@QueryParam';
11
+ /* Il riepilogo è lo stesso nel tooltip e nel dettaglio: cambia solo lo spazio a disposizione.
12
+ Nel tooltip la larghezza è limitata perché una riga lunghissima è illeggibile, nel dettaglio il contenuto scorre */
13
+ const StyledSummary = styled.div `
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 8px;
17
+ ${props => props.$isDetail ? 'height: 100%; overflow-y: auto;' : 'min-width: 320px; max-width: 580px;'}
18
+ padding: ${props => props.$isDetail ? '14px 18px' : '10px 12px'};
19
+ text-align: left;
20
+ /* Nel tooltip il testo è un po' più piccolo: è una lettura di passaggio, il dettaglio è quello da leggere con calma */
21
+ font-size: ${props => props.$isDetail ? '1rem' : '0.9375rem'};
22
+ line-height: 1.5;
23
+ color: ${TMColors.text_normal};
24
+ `;
25
+ /* Intestazione del tooltip: nel dettaglio il nome della ricerca è già nel titolo del modale */
26
+ const StyledSummaryTitle = styled.div `
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 7px;
30
+ padding-bottom: 8px;
31
+ border-bottom: 1px solid ${TMColors.border_normal};
32
+ font-size: 1.0625em;
33
+ font-weight: 600;
34
+ color: ${TMColors.primary};
35
+
36
+ svg { flex-shrink: 0; }
37
+ `;
38
+ const StyledSectionTitle = styled.div `
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 7px;
42
+ font-size: 0.8125em;
43
+ font-weight: 700;
44
+ text-transform: uppercase;
45
+ letter-spacing: 0.5px;
46
+ color: ${TMColors.label_normal};
47
+ `;
48
+ const StyledCounter = styled.span `
49
+ display: inline-flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ min-width: 21px;
53
+ height: 21px;
54
+ padding: 0 6px;
55
+ border-radius: 11px;
56
+ background-color: ${TMColors.primary};
57
+ color: white;
58
+ font-weight: 600;
59
+ letter-spacing: 0;
60
+ `;
61
+ const StyledConditions = styled.div `display: flex; flex-direction: column; gap: 5px;`;
62
+ /* Ogni condizione è una riga a sé: il fondo e il filetto colorato la staccano dalle altre e rendono
63
+ leggibile l'elenco anche quando le condizioni sono molte */
64
+ const StyledCondition = styled.div `
65
+ display: flex;
66
+ flex-direction: row;
67
+ align-items: baseline;
68
+ flex-wrap: wrap;
69
+ gap: 7px;
70
+ padding: 7px 10px;
71
+ border-left: 3px solid ${TMColors.primary};
72
+ border-radius: 4px;
73
+ background-color: ${TMColors.toolbar_background};
74
+ `;
75
+ /* La congiunzione con la condizione precedente: a larghezza fissa, così i nomi dei metadati restano incolonnati */
76
+ const StyledConjunction = styled.span `
77
+ flex-shrink: 0;
78
+ width: 42px;
79
+ font-size: 0.8125em;
80
+ font-weight: 700;
81
+ letter-spacing: 0.5px;
82
+ color: ${TMColors.label_normal};
83
+ `;
84
+ /* Nome del metadato, operatore e valore restano sulla stessa riga finché c'è spazio, poi vanno a capo insieme */
85
+ const StyledConditionText = styled.span `
86
+ display: flex;
87
+ flex: 1;
88
+ flex-direction: row;
89
+ flex-wrap: wrap;
90
+ align-items: baseline;
91
+ gap: 5px;
92
+ min-width: 0;
93
+ `;
94
+ const StyledMetadataName = styled.span `font-weight: 600; word-break: break-word;`;
95
+ const StyledOperator = styled.span `color: ${TMColors.label_normal}; white-space: nowrap;`;
96
+ const StyledValue = styled.span `
97
+ padding: 2px 7px;
98
+ border: 1px solid ${TMColors.border_normal};
99
+ border-radius: 3px;
100
+ background-color: ${TMColors.default_background};
101
+ font-weight: 600;
102
+ color: ${TMColors.primary};
103
+ word-break: break-word;
104
+ `;
105
+ /* Il valore chiesto a ogni esecuzione non è un valore di ricerca: il bordo tratteggiato dice che è ancora da riempire */
106
+ const StyledParameter = styled(StyledValue) `
107
+ border-style: dashed;
108
+ border-color: ${TMColors.tertiary};
109
+ background-color: transparent;
110
+ font-style: italic;
111
+ font-weight: normal;
112
+ color: ${TMColors.tertiary};
113
+ `;
114
+ const StyledNote = styled.div `
115
+ display: flex;
116
+ align-items: center;
117
+ gap: 6px;
118
+ font-size: 0.875em;
119
+ color: ${TMColors.label_normal};
120
+
121
+ svg { flex-shrink: 0; }
122
+ `;
123
+ /* Invito ad aprire il dettaglio: nel tooltip il testo è più piccolo e l'elenco può essere troncato */
124
+ const StyledHint = styled(StyledNote) `
125
+ padding-top: 7px;
126
+ border-top: 1px solid ${TMColors.border_normal};
127
+ color: ${TMColors.primary};
128
+ font-weight: 600;
129
+ `;
130
+ const getDcmtTypeName = (dtd) => (SDK_Globals.useLocalizedName ? dtd?.nameLoc : dtd?.name) ?? dtd?.name ?? dtd?.nameLoc ?? undefined;
131
+ const getMetadataName = (md) => (SDK_Globals.useLocalizedName ? md?.nameLoc : md?.name) ?? md?.name ?? md?.nameLoc ?? undefined;
132
+ const isQueryParam = (value) => Boolean(value?.startsWith(QUERY_PARAM_PREFIX));
133
+ /** Valori scritti nella condizione: gli operatori a elenco ne contengono più di uno, separati da virgola e fra apici */
134
+ const splitValues = (value) => (value ?? '').split(',').map(item => {
135
+ const trimmed = item.trim();
136
+ return trimmed.startsWith("'") && trimmed.endsWith("'") ? trimmed.slice(1, -1) : trimmed;
137
+ });
138
+ /* Ogni condizione dell'editor di ricerca nasce racchiusa in una coppia di parentesi che non raggruppa nulla:
139
+ nel riepilogo sono solo rumore, restano quelle che aprono o chiudono un raggruppamento vero */
140
+ const meaningfulBrackets = (brackets) => (brackets && brackets.length > 1) ? brackets : undefined;
141
+ const findMetadata = (dcmtTypes, tid, mid) => dcmtTypes.find(o => o.id === tid)?.metadata?.find(o => o.id === mid);
142
+ const formatValue = (md, value, getDisplayValue) => {
143
+ // Il segnaposto di un parametro non è un valore del metadato: va mostrato com'è
144
+ if (isQueryParam(value))
145
+ return value;
146
+ /* Liste dati e utenti sono archiviati per chiave: nel riepilogo va la descrizione, come nelle griglie
147
+ del pannello, altrimenti il filtro resterebbe illeggibile (es. TD01 invece di FATTURA) */
148
+ return splitValues(value)
149
+ .map(o => needsDisplayValue(md) ? String(getDisplayValue(md, o) ?? o) : displayMetadataValue(md, o))
150
+ .join(', ');
151
+ };
152
+ /**
153
+ * Valore mostrato per la condizione: il numero di operandi dell'operatore dice quali valori sono significativi
154
+ * e come vanno letti, perché non tutti sono valori del metadato.
155
+ */
156
+ const buildConditionValue = (wi, md, operands, getDisplayValue) => {
157
+ switch (operands) {
158
+ // L'operatore non ha valori (è nullo, oggi, questo mese, ...): la condizione è tutta nell'operatore
159
+ case 0: return undefined;
160
+ // Numero di ore/giorni/... e condizione scritta a mano: non sono valori del metadato e vanno lasciati come sono
161
+ case 12:
162
+ case 99: return wi.value1;
163
+ case 2: return `${formatValue(md, wi.value1, getDisplayValue)} … ${formatValue(md, wi.value2, getDisplayValue)}`;
164
+ // Valore singolo o elenco di valori: in entrambi i casi sono valori del metadato
165
+ default: return formatValue(md, wi.value1, getDisplayValue);
166
+ }
167
+ };
168
+ const buildCondition = (wi, md, index, dcmtTypes, fromTid, getDisplayValue) => {
169
+ const operands = PlatformObjectValidator.GetNumberOfOperands(wi.operator);
170
+ return {
171
+ conjunction: index <= 0 ? undefined : (wi.or ? 'OR' : 'AND'),
172
+ leftBrackets: meaningfulBrackets(wi.leftBrackets),
173
+ rightBrackets: meaningfulBrackets(wi.rightBrackets),
174
+ // Con i join il nome del metadato da solo non basta a capire su quale tipo documento si sta filtrando
175
+ dcmtTypeName: wi.tid && wi.tid !== fromTid ? (wi.alias ?? getDcmtTypeName(dcmtTypes.find(o => o.id === wi.tid))) : undefined,
176
+ metadataName: getMetadataName(md) ?? `MID ${wi.mid ?? 0}`,
177
+ operator: LocalizeQueryOperators(wi.operator),
178
+ value: buildConditionValue(wi, md, operands, getDisplayValue),
179
+ isParameter: isQueryParam(wi.value1) || isQueryParam(wi.value2),
180
+ };
181
+ };
182
+ /**
183
+ * Filtri effettivamente applicati dalla ricerca: in modalità easy quelli senza valore vengono scartati prima
184
+ * della ricerca (sono i campi lasciati vuoti dall'utente) e mostrarli farebbe credere che la ricerca filtri
185
+ * su tutti i metadati, che è il caso di "Tutti i documenti".
186
+ */
187
+ const getEffectiveWhere = (sqd, qd) => sqd?.isEasyWhere === 1
188
+ ? (qd?.where ?? []).filter(o => PlatformObjectValidator.WhereItemHasValues(o))
189
+ : (qd?.where ?? []);
190
+ /**
191
+ * Righe da cui vengono ricavate le chiavi per cui caricare le descrizioni: hanno la stessa forma delle righe
192
+ * di un risultato di ricerca (un valore per condizione), perché è quella attesa dalle cache del pannello.
193
+ */
194
+ const buildDisplayRows = (where) => {
195
+ const rows = [];
196
+ where.forEach((wi, index) => {
197
+ [...splitValues(wi.value1), ...splitValues(wi.value2)].forEach((value, valueIndex) => {
198
+ rows[valueIndex] ??= [];
199
+ rows[valueIndex][index] = value;
200
+ });
201
+ });
202
+ return rows;
203
+ };
204
+ const TMQuickSearchCondition = ({ condition }) => (_jsxs(StyledCondition, { children: [_jsx(StyledConjunction, { children: condition.conjunction }), _jsxs(StyledConditionText, { children: [condition.leftBrackets && _jsx(StyledOperator, { children: condition.leftBrackets }), _jsxs(StyledMetadataName, { children: [condition.dcmtTypeName && _jsxs(StyledOperator, { children: [condition.dcmtTypeName, "."] }), condition.metadataName] }), _jsx(StyledOperator, { children: condition.operator }), condition.value && (condition.isParameter
205
+ ? _jsx(StyledParameter, { children: condition.value })
206
+ : _jsx(StyledValue, { children: condition.value })), condition.rightBrackets && _jsx(StyledOperator, { children: condition.rightBrackets })] })] }));
207
+ /** Riepilogo mostrato nel tooltip e nel dettaglio: nel dettaglio il nome della ricerca è già nel titolo del modale */
208
+ const TMQuickSearchSummary = ({ summary, isDetail = false }) => {
209
+ if (!summary) {
210
+ return (_jsx(StyledSummary, { "$isDetail": isDetail, children: _jsxs(StyledNote, { children: [SDKUI_Localizator.Loading, " ..."] }) }));
211
+ }
212
+ if (summary.isUnavailable) {
213
+ return (_jsxs(StyledSummary, { "$isDetail": isDetail, children: [!isDetail && _jsxs(StyledSummaryTitle, { children: [_jsx(IconSavedQuery, { fontSize: 19 }), summary.name] }), _jsxs(StyledNote, { children: [_jsx(IconInfo, { fontSize: 17 }), "La ricerca rapida configurata non \u00E8 pi\u00F9 disponibile"] })] }));
214
+ }
215
+ // Nel tooltip le condizioni oltre il limite vengono troncate: il tooltip non è scorribile
216
+ const shownConditions = isDetail ? summary.conditions : summary.conditions.slice(0, TOOLTIP_MAX_CONDITIONS);
217
+ const hiddenConditionsCount = summary.conditions.length - shownConditions.length;
218
+ const hasParameters = shownConditions.some(o => o.isParameter);
219
+ return (_jsxs(StyledSummary, { "$isDetail": isDetail, children: [!isDetail && _jsxs(StyledSummaryTitle, { children: [_jsx(IconSavedQuery, { fontSize: 19 }), summary.name] }), summary.conditions.length <= 0
220
+ ? _jsxs(StyledNote, { children: [_jsx(IconInfo, { fontSize: 17 }), "Nessun filtro: la ricerca restituisce tutti i documenti"] })
221
+ : _jsxs(_Fragment, { children: [_jsxs(StyledSectionTitle, { children: [SDK_Localizator.QueryWhere, _jsx(StyledCounter, { children: summary.conditions.length })] }), _jsx(StyledConditions, { children: shownConditions.map((condition, index) => _jsx(TMQuickSearchCondition, { condition: condition }, index)) }), hiddenConditionsCount > 0 &&
222
+ _jsxs(StyledNote, { children: [_jsx(IconInfo, { fontSize: 17 }), "Altre ", hiddenConditionsCount, " condizioni non mostrate"] })] }), hasParameters && _jsxs(StyledNote, { children: [_jsx(IconInfo, { fontSize: 17 }), "I valori tratteggiati vengono chiesti a ogni esecuzione della ricerca"] }), !isDetail && _jsxs(StyledHint, { children: [_jsx(IconInfo, { fontSize: 17 }), "Fare clic per aprire il dettaglio e leggerlo meglio"] })] }));
223
+ };
224
+ export const useTMQuickSearchInfo = ({ tid, sqdId }) => {
225
+ // Le descrizioni di liste dati e utenti vengono dalle stesse cache usate dalle griglie del pannello
226
+ const { loadDisplayCachesAsync, getMetadataDisplayValue } = useTMDistinctValuesMetadataDisplay();
227
+ const [summary, setSummary] = useState();
228
+ const [showDetail, setShowDetail] = useState(false);
229
+ // Identifica la richiesta corrente: dopo ogni await le risposte delle richieste obsolete vengono scartate
230
+ const requestIdRef = useRef(0);
231
+ useEffect(() => { loadAsync(); }, [tid, sqdId]);
232
+ const loadAsync = async () => {
233
+ const requestId = ++requestIdRef.current;
234
+ const isStale = () => requestId !== requestIdRef.current;
235
+ setSummary(undefined);
236
+ if (!sqdId)
237
+ return;
238
+ try {
239
+ const sqd = sqdId === SYS_ALL_DCMTS_SQD_ID
240
+ ? await getSysAllDcmtsSQD(tid, false)
241
+ : await SavedQueryCacheService.GetAsync(sqdId);
242
+ const qd = SearchEngine.NormalizeQueryDescriptor(sqd?.qd);
243
+ const name = sqd?.name ?? (sqd?.id ? `ID ${sqd.id}` : SDK_Localizator.SavedQuery);
244
+ if (!qd) {
245
+ if (!isStale())
246
+ setSummary({ name, conditions: [], isUnavailable: true });
247
+ return;
248
+ }
249
+ // I nomi dei metadati stanno sui tipi documento della ricerca: con i join le condizioni ne riguardano più di uno
250
+ const dcmtTypes = await DcmtTypeListCacheService.GetFromQdAsync(qd);
251
+ const where = getEffectiveWhere(sqd, qd);
252
+ const whereMd = where.map(wi => findMetadata(dcmtTypes, wi.tid, wi.mid));
253
+ // Le descrizioni si leggono dalle cache: vanno caricate prima di comporre il riepilogo
254
+ await loadDisplayCachesAsync(whereMd, buildDisplayRows(where));
255
+ if (isStale())
256
+ return;
257
+ setSummary({
258
+ name,
259
+ conditions: where.map((wi, index) => buildCondition(wi, whereMd[index], index, dcmtTypes, qd.from?.tid, getMetadataDisplayValue)),
260
+ isUnavailable: false,
261
+ });
262
+ }
263
+ catch {
264
+ /* Il riepilogo è un di più: un errore qui non deve disturbare con una finestra di eccezione,
265
+ basta dire nel tooltip che la ricerca non è leggibile */
266
+ if (!isStale())
267
+ setSummary({ name: SDK_Localizator.SavedQuery, conditions: [], isUnavailable: true });
268
+ }
269
+ };
270
+ const detailTitle = useMemo(() => `${SDK_Localizator.SavedQuery}${summary?.name ? ` - ${summary.name}` : ''}`, [summary?.name]);
271
+ const infoButton = !sqdId ? undefined : {
272
+ icon: _jsx(IconInfo, {}),
273
+ text: SDK_Localizator.SavedQuery,
274
+ tooltipContent: _jsx(TMQuickSearchSummary, { summary: summary }),
275
+ onClick: () => setShowDetail(true),
276
+ };
277
+ const detailForm = !showDetail ? null : (_jsx(TMModal, { title: detailTitle, width: '620px', height: '520px', onClose: () => setShowDetail(false), children: _jsx(TMQuickSearchSummary, { summary: summary, isDetail: true }) }));
278
+ return { infoButton, detailForm };
279
+ };
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { DistinctValuesQuickSearchSettings } from '../../helper';
3
+ /** Impostazione della ricerca rapida usata dal pannello dei valori distinti: è la stessa che viene persistita nelle impostazioni utente */
4
+ export type QuickSearchSettings = DistinctValuesQuickSearchSettings;
5
+ /** Impostazione vuota da cui parte la configurazione: solo il numero massimo di documenti ha un valore predefinito */
6
+ export declare const newQuickSearchSettings: () => QuickSearchSettings;
7
+ interface ITMQuickSearchSettingsForm {
8
+ /** Impostazione di partenza: se assente il form parte vuoto */
9
+ settings?: QuickSearchSettings;
10
+ onSave?: (settings: QuickSearchSettings) => void;
11
+ /** Chiede l'eliminazione dell'impostazione salvata: la conferma spetta al chiamante. Senza il gestore il pulsante di eliminazione non viene mostrato */
12
+ onDelete?: () => void;
13
+ onClose?: () => void;
14
+ }
15
+ declare const TMQuickSearchSettingsForm: React.FC<ITMQuickSearchSettingsForm>;
16
+ export default TMQuickSearchSettingsForm;