@topconsultnpm/sdkui-react 6.21.0-dev2.2 → 6.21.0-dev2.21

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 (42) hide show
  1. package/lib/components/base/TMAccordionNew.d.ts +1 -0
  2. package/lib/components/base/TMAccordionNew.js +6 -5
  3. package/lib/components/base/TMAreaManager.js +19 -3
  4. package/lib/components/base/TMPanel.d.ts +7 -4
  5. package/lib/components/base/TMPanel.js +54 -26
  6. package/lib/components/choosers/TMDistinctValues.js +35 -21
  7. package/lib/components/editors/TMDateBox.js +4 -2
  8. package/lib/components/editors/TMFormulaEditor.d.ts +2 -0
  9. package/lib/components/editors/TMFormulaEditor.js +75 -21
  10. package/lib/components/editors/TMMetadataValues.js +2 -1
  11. package/lib/components/editors/TMRadioButton.js +2 -1
  12. package/lib/components/editors/TMTextArea.d.ts +2 -0
  13. package/lib/components/editors/TMTextArea.js +6 -3
  14. package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
  15. package/lib/components/features/documents/TMDcmtForm.js +40 -10
  16. package/lib/components/features/documents/TMDcmtFormActionButtons.js +17 -2
  17. package/lib/components/features/documents/TMDcmtPreview.d.ts +1 -0
  18. package/lib/components/features/documents/TMDcmtPreview.js +2 -2
  19. package/lib/components/features/documents/TMDcmtTasks.d.ts +1 -0
  20. package/lib/components/features/documents/TMDcmtTasks.js +2 -2
  21. package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
  22. package/lib/components/features/search/TMSavedQuerySelector.js +3 -2
  23. package/lib/components/features/search/TMSearch.d.ts +2 -1
  24. package/lib/components/features/search/TMSearch.js +15 -9
  25. package/lib/components/features/search/TMSearchQueryPanel.js +1 -1
  26. package/lib/components/features/search/TMSearchResult.js +56 -14
  27. package/lib/components/features/search/TMViewHistoryDcmt.js +1 -2
  28. package/lib/components/features/workflow/diagram/queryDescriptorParser.js +3 -6
  29. package/lib/components/grids/TMBlogAttachments.d.ts +1 -0
  30. package/lib/components/grids/TMBlogAttachments.js +38 -12
  31. package/lib/components/grids/TMBlogsPost.js +7 -1
  32. package/lib/components/grids/TMBlogsPostUtils.js +11 -17
  33. package/lib/components/pages/TMPage.js +3 -1
  34. package/lib/helper/GlobalStyles.js +6 -0
  35. package/lib/helper/SDKUI_Localizator.d.ts +48 -0
  36. package/lib/helper/SDKUI_Localizator.js +482 -0
  37. package/lib/helper/TMPdfViewer.js +25 -24
  38. package/lib/hooks/useDataUserIdItem.js +6 -4
  39. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  40. package/lib/hooks/useDocumentOperations.js +8 -6
  41. package/lib/hooks/useForm.js +3 -0
  42. package/package.json +54 -54
@@ -23,6 +23,7 @@ export interface TMAccordionProps {
23
23
  localStorageKey?: string;
24
24
  selectedItem?: any;
25
25
  onSelectedItemChange?: (item: any) => void;
26
+ itemMarginBottom?: number;
26
27
  }
27
28
  declare const TMAccordion: React.FC<TMAccordionProps>;
28
29
  export default TMAccordion;
@@ -41,7 +41,7 @@ const Container = styled.div `
41
41
  }
42
42
  `;
43
43
  const AccordionItem = styled.div `
44
- margin-bottom: 16px;
44
+ margin-bottom: ${props => props.$marginBottom}px;
45
45
  border-radius: 5px;
46
46
  background: white;
47
47
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
@@ -152,7 +152,7 @@ const LoadingContainer = styled.div `
152
152
  min-height: 200px;
153
153
  `;
154
154
  const DEFAULT_COLORS = ['#339af0', '#fa5252', '#40c057', '#f59f00', '#e64980', '#7950f2'];
155
- const TMAccordion = ({ groups, localStorageKey, selectedItem, onSelectedItemChange }) => {
155
+ const TMAccordion = ({ groups, localStorageKey, selectedItem, onSelectedItemChange, itemMarginBottom = 16 }) => {
156
156
  const [openAccordions, setOpenAccordions] = useState({});
157
157
  const [isInitialized, setIsInitialized] = useState(false);
158
158
  const [focusedItem, setFocusedItem] = useState(undefined);
@@ -252,6 +252,7 @@ const TMAccordion = ({ groups, localStorageKey, selectedItem, onSelectedItemChan
252
252
  disabled: mi.disabled,
253
253
  onClick: mi.onClick,
254
254
  submenu: mi.items || mi.submenu,
255
+ beginGroup: mi.beginGroup ?? false
255
256
  }));
256
257
  setItemContextMenuPosition({ x: event.clientX, y: event.clientY });
257
258
  setItemContextMenuItems(convertedItems);
@@ -308,16 +309,16 @@ const TMAccordion = ({ groups, localStorageKey, selectedItem, onSelectedItemChan
308
309
  const color = getGroupColor(group, index);
309
310
  const isOpen = openAccordions[group.id] ?? true;
310
311
  if (group.customComponent) {
311
- return (_jsxs(AccordionItem, { children: [_jsxs(AccordionHeader, { id: `accordion-header-${group.id}`, "$color": color, "$isOpen": isOpen, onClick: (e) => handleHeaderClick(e, group.id), onContextMenu: (e) => handleHeaderContextMenu(e, group.id), children: [_jsxs(AccordionTitle, { children: [group.icon, _jsx(TitleText, { children: group.title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [renderBadges(group, color), _jsx(ToggleIcon, { "$isOpen": isOpen, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "#495057", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), isOpen && (_jsx(CustomComponentContainer, { children: group.customComponent }))] }, group.id));
312
+ return (_jsxs(AccordionItem, { "$marginBottom": itemMarginBottom, children: [_jsxs(AccordionHeader, { id: `accordion-header-${group.id}`, "$color": color, "$isOpen": isOpen, onClick: (e) => handleHeaderClick(e, group.id), onContextMenu: (e) => handleHeaderContextMenu(e, group.id), children: [_jsxs(AccordionTitle, { children: [group.icon, _jsx(TitleText, { children: group.title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [renderBadges(group, color), _jsx(ToggleIcon, { "$isOpen": isOpen, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "#495057", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), isOpen && (_jsx(CustomComponentContainer, { children: group.customComponent }))] }, group.id));
312
313
  }
313
314
  if (!group.dataSource || group.dataSource.length === 0)
314
315
  return null;
315
316
  if (!group.renderItem) {
316
- return (_jsxs(AccordionItem, { children: [_jsxs(AccordionHeader, { id: `accordion-header-${group.id}`, "$color": color, "$isOpen": isOpen, onClick: (e) => handleHeaderClick(e, group.id), onContextMenu: (e) => handleHeaderContextMenu(e, group.id), children: [_jsxs(AccordionTitle, { children: [group.icon, _jsx(TitleText, { children: group.title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [renderBadges(group, color), _jsx(ToggleIcon, { "$isOpen": isOpen, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "#495057", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), _jsx(AccordionContent, { "$isOpen": isOpen, "$maxHeight": 100, children: _jsxs(ErrorMessage, { children: ["\u26A0\uFE0F renderItem function is required for \"", group.title, "\" group"] }) })] }, group.id));
317
+ return (_jsxs(AccordionItem, { "$marginBottom": itemMarginBottom, children: [_jsxs(AccordionHeader, { id: `accordion-header-${group.id}`, "$color": color, "$isOpen": isOpen, onClick: (e) => handleHeaderClick(e, group.id), onContextMenu: (e) => handleHeaderContextMenu(e, group.id), children: [_jsxs(AccordionTitle, { children: [group.icon, _jsx(TitleText, { children: group.title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [renderBadges(group, color), _jsx(ToggleIcon, { "$isOpen": isOpen, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "#495057", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), _jsx(AccordionContent, { "$isOpen": isOpen, "$maxHeight": 100, children: _jsxs(ErrorMessage, { children: ["\u26A0\uFE0F renderItem function is required for \"", group.title, "\" group"] }) })] }, group.id));
317
318
  }
318
319
  const itemHeight = group.itemHeight || 80;
319
320
  const maxHeight = group.dataSource.length * itemHeight + 20;
320
- return (_jsxs(AccordionItem, { children: [_jsxs(AccordionHeader, { id: `accordion-header-${group.id}`, "$color": color, "$isOpen": isOpen, onClick: (e) => handleHeaderClick(e, group.id), onContextMenu: (e) => handleHeaderContextMenu(e, group.id), children: [_jsxs(AccordionTitle, { children: [group.icon, _jsx(TitleText, { children: group.title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [renderBadges(group, color), _jsx(ToggleIcon, { "$isOpen": isOpen, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "#495057", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), _jsx(AccordionContent, { "$isOpen": isOpen, "$maxHeight": maxHeight, children: _jsx(ItemsList, { children: group.dataSource.map((item, itemIndex) => {
321
+ return (_jsxs(AccordionItem, { "$marginBottom": itemMarginBottom, children: [_jsxs(AccordionHeader, { id: `accordion-header-${group.id}`, "$color": color, "$isOpen": isOpen, onClick: (e) => handleHeaderClick(e, group.id), onContextMenu: (e) => handleHeaderContextMenu(e, group.id), children: [_jsxs(AccordionTitle, { children: [group.icon, _jsx(TitleText, { children: group.title })] }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [renderBadges(group, color), _jsx(ToggleIcon, { "$isOpen": isOpen, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: _jsx("path", { d: "M4 6L8 10L12 6", stroke: "#495057", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), _jsx(AccordionContent, { "$isOpen": isOpen, "$maxHeight": maxHeight, children: _jsx(ItemsList, { children: group.dataSource.map((item, itemIndex) => {
321
322
  const isSelected = selectedItem === item;
322
323
  const isFocused = focusedItem === item;
323
324
  return (_jsx(ListItem, { id: `accordion-item-${group.id}-${itemIndex}`, className: "tm-accordion-list-item", "$isSelected": isSelected, "$color": color, onClick: () => handleItemClick(item, group), onDoubleClick: () => handleItemDoubleClick(item, group), onMouseEnter: () => setFocusedItem(item), onMouseLeave: () => setFocusedItem(undefined), onContextMenu: (e) => handleContextMenu(e, item, group), children: group.renderItem(item, isSelected, isFocused, color) }, itemIndex * 2));
@@ -5,7 +5,7 @@ import { FileDescriptor, FileFormats, FileTransferModes, SDK_Localizator, SDK_Gl
5
5
  import CustomFileSystemProvider from 'devextreme/file_management/custom_provider';
6
6
  import FileSystemError from "devextreme/file_management/error";
7
7
  import Button from "devextreme/ui/button";
8
- import { alert, confirm } from "devextreme/ui/dialog";
8
+ import { alert, custom } from "devextreme/ui/dialog";
9
9
  import { loadMessages } from 'devextreme/localization';
10
10
  import { getFileManagerFolderColor, Globalization, IconAll, IconCloud, IconFolder, IconFolderForCSS, IconSelected, SDKUI_Localizator, svgToString } from '../../helper';
11
11
  import { TMExceptionBoxManager } from './TMPopUp';
@@ -253,8 +253,24 @@ const TMAreaManager = (props = { selectionMode: 'multiple', isPathChooser: false
253
253
  else {
254
254
  msg = SDKUI_Localizator.FileManager_QuestionAlreadyExistsFiles.replaceParams(resolvesForExistingFiles.length.toString());
255
255
  }
256
- const result = confirm(msg, SDKUI_Localizator.Attention);
257
- result.then((dialogResult) => {
256
+ const confirmDialog = custom({
257
+ title: SDKUI_Localizator.Attention,
258
+ messageHtml: `<div style="padding: 15px; font-size: 1rem; max-width: 500px; word-wrap: break-word; overflow-wrap: break-word;">${msg}</div>`,
259
+ buttons: [
260
+ {
261
+ text: SDKUI_Localizator.Yes ?? "Yes",
262
+ onClick: () => true,
263
+ type: "default",
264
+ stylingMode: "contained"
265
+ },
266
+ {
267
+ text: SDKUI_Localizator.No ?? "No",
268
+ onClick: () => false,
269
+ stylingMode: "outlined"
270
+ }
271
+ ]
272
+ });
273
+ confirmDialog.show().then((dialogResult) => {
258
274
  resolvesForNonExistingFiles.forEach((res) => {
259
275
  res({
260
276
  cancel: false,
@@ -1,7 +1,3 @@
1
- /** Gestione TMPanel "attivo / non attivo" secondo una pattern ibrido (Controlled/Uncontrolled Component):
2
- 1) Modalità Non Controllata (Uncontrolled): Se non gli viene passata la prop "isActive", il pannello gestisce lo stato "attivo" internamente. Per l'uso singolo ("plug-and-play").
3
- 2) Modalità Controllata (Controlled): Se il genitore passa la prop "isActive", il pannello cede il controllo e si affida completamente al genitore. Ideale per layout con più pannelli che necessitano di coordinamento.
4
- */
5
1
  import React from 'react';
6
2
  export interface ITMPanelRef {
7
3
  focusPanel: () => void;
@@ -28,5 +24,12 @@ export interface ITMPanelProps {
28
24
  onHeaderDoubleClick?: () => void;
29
25
  onMaximize?: (isMaximized: boolean) => void;
30
26
  }
27
+ /**
28
+ * TMPanel - Pannello con supporto Controlled/Uncontrolled
29
+ *
30
+ * @prop isActive (opzionale)
31
+ * - Se NON passata → Uncontrolled: gestione interna dello stato attivo (plug-and-play)
32
+ * - Se passata → Controlled: il parent gestisce lo stato tramite onActivate
33
+ */
31
34
  declare const TMPanel: React.ForwardRefExoticComponent<ITMPanelProps & React.RefAttributes<ITMPanelRef>>;
32
35
  export default TMPanel;
@@ -1,13 +1,33 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /** Gestione TMPanel "attivo / non attivo" secondo una pattern ibrido (Controlled/Uncontrolled Component):
3
- 1) Modalità Non Controllata (Uncontrolled): Se non gli viene passata la prop "isActive", il pannello gestisce lo stato "attivo" internamente. Per l'uso singolo ("plug-and-play").
4
- 2) Modalità Controllata (Controlled): Se il genitore passa la prop "isActive", il pannello cede il controllo e si affida completamente al genitore. Ideale per layout con più pannelli che necessitano di coordinamento.
5
- */
6
2
  import { useCallback, useRef, useState, forwardRef, useImperativeHandle } from 'react';
7
3
  import styled from 'styled-components';
8
4
  import { getAppModuleGradient, IconArrowLeft, IconClearButton, IconWindowMaximize, IconWindowMinimize, isPositiveNumber, SDKUI_Localizator } from '../../helper';
9
5
  import TMButton from './TMButton';
10
6
  import { Gutters } from '../../utils/theme';
7
+ const StyledBackButton = styled.button `
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ gap: 4px;
12
+ padding: 4px 10px;
13
+ border: 1px solid ${({ $isActive }) => $isActive ? 'rgba(255, 255, 255, 0.4)' : 'rgba(37, 89, 165, 0.3)'};
14
+ border-radius: 6px;
15
+ cursor: pointer;
16
+ font-weight: 500;
17
+ font-size: 0.85rem;
18
+ flex-shrink: 0;
19
+ transition: all 0.15s ease;
20
+ background-color: #2559A5;
21
+ box-shadow: none;
22
+
23
+ &&, && * {
24
+ color: #FFFFFF !important;
25
+ }
26
+
27
+ svg {
28
+ font-size: 14px;
29
+ }
30
+ `;
11
31
  import { AppModules, SDK_Globals } from '@topconsultnpm/sdk-ts';
12
32
  const StyledPanelContainer = styled.div `
13
33
  width: 100%;
@@ -75,6 +95,13 @@ const StyledPanelContent = styled.div `
75
95
  outline: none;
76
96
  }
77
97
  `;
98
+ /**
99
+ * TMPanel - Pannello con supporto Controlled/Uncontrolled
100
+ *
101
+ * @prop isActive (opzionale)
102
+ * - Se NON passata → Uncontrolled: gestione interna dello stato attivo (plug-and-play)
103
+ * - Se passata → Controlled: il parent gestisce lo stato tramite onActivate
104
+ */
78
105
  const TMPanel = forwardRef(({ allowMaximize = true, color, backgroundColor, backgroundColorContainer, children, showHeader = true, title, totalItems, displayedItemsCount, toolbar, padding = '5px', isVisible = true, panelID = 'tmpanel', isActive, // Questa prop determinerà la modalità
79
106
  onActivate, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged }, ref) => {
80
107
  const [isMaximized, setIsMaximized] = useState(false);
@@ -149,36 +176,37 @@ onActivate, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged },
149
176
  flexDirection: 'row',
150
177
  alignItems: 'center',
151
178
  width: '100%',
152
- minWidth: 0
153
- }, children: [_jsxs("div", { style: {
179
+ minWidth: 0,
180
+ flexWrap: 'nowrap',
181
+ overflow: 'hidden'
182
+ }, children: [onBack && _jsx("div", { style: {
154
183
  display: 'flex',
155
184
  flexDirection: 'row',
156
185
  alignItems: 'center',
157
- gap: '8px',
186
+ flexShrink: 0
187
+ }, children: _jsxs(StyledBackButton, { "$isActive": currentIsActive, onClick: () => onBack ? onBack() : null, title: SDKUI_Localizator.Back, children: [_jsx(IconArrowLeft, {}), _jsx("span", { children: SDKUI_Localizator.Back })] }) }), _jsx("div", { style: {
158
188
  flex: 1,
159
- minWidth: 0
160
- }, children: [onBack &&
161
- _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconArrowLeft, {}), caption: SDKUI_Localizator.Back, onClick: onBack }), _jsx("div", { style: {
162
- display: 'flex',
163
- flexDirection: 'row',
164
- alignItems: 'center',
165
- overflow: 'hidden',
166
- }, children: _jsxs("div", { style: {
167
- whiteSpace: 'nowrap',
168
- overflow: 'hidden',
169
- textOverflow: 'ellipsis',
170
- margin: 0
171
- }, children: [title, isPositiveNumber(displayedItemsCount) && isPositiveNumber(totalItems)
172
- ? ` (${displayedItemsCount} / ${totalItems})`
173
- : isPositiveNumber(totalItems)
174
- ? ` (${totalItems})`
175
- : ''] }) })] }), _jsxs("div", { style: {
189
+ display: 'flex',
190
+ alignItems: 'center',
191
+ justifyContent: 'flex-start',
192
+ minWidth: 0,
193
+ overflow: 'hidden',
194
+ padding: '0 8px'
195
+ }, children: _jsxs("div", { style: {
196
+ whiteSpace: 'nowrap',
197
+ overflow: 'hidden',
198
+ textOverflow: 'ellipsis',
199
+ margin: 0
200
+ }, children: [title, isPositiveNumber(displayedItemsCount) && isPositiveNumber(totalItems)
201
+ ? ` (${displayedItemsCount} / ${totalItems})`
202
+ : isPositiveNumber(totalItems)
203
+ ? ` (${totalItems})`
204
+ : ''] }) }), _jsxs("div", { style: {
176
205
  display: 'flex',
177
206
  flexDirection: 'row',
178
207
  alignItems: 'center',
179
208
  gap: '5px',
180
- flexShrink: 0,
181
- marginLeft: 10
209
+ flexShrink: 0
182
210
  }, children: [toolbar, allowMaximize && _jsx(TMButton, { color: 'primaryOutline', caption: isMaximized ? SDKUI_Localizator.Minimize : SDKUI_Localizator.Maximize, icon: isMaximized
183
211
  ? _jsx(IconWindowMinimize, { fontSize: 16 })
184
212
  : _jsx(IconWindowMaximize, { fontSize: 16 }), btnStyle: 'icon', onClick: handleMaximize }), onClose && _jsx(TMButton, { color: 'primaryOutline', caption: SDKUI_Localizator.Close, icon: _jsx(IconClearButton, {}), btnStyle: 'icon', onClick: () => { setIsMaximized(false); onClose?.(); } })] })] }) }), _jsx(StyledPanelContent, { "$height": showHeader ? "calc(100% - 40px)" : "100%", "$padding": padding, "$backgroundColor": backgroundColorContainer ?? `#FFFFFF`, children: children })] }));
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useState } from 'react';
3
- import { LayoutModes, MetadataDataDomains, AccessLevels, MetadataDataTypes, SDK_Globals, DcmtTypeListCacheService, DataColumnTypes } from '@topconsultnpm/sdk-ts';
3
+ import { LayoutModes, MetadataDataDomains, AccessLevels, MetadataDataTypes, SDK_Globals, DcmtTypeListCacheService, DataColumnTypes, DataListViewModes } from '@topconsultnpm/sdk-ts';
4
4
  import styled from 'styled-components';
5
5
  import { SDKUI_Localizator, stringIsNullOrEmpty } from '../../helper';
6
6
  import TMDataGrid, { TMDataGridPageSize } from '../base/TMDataGrid';
@@ -9,14 +9,17 @@ import TMSpinner from '../base/TMSpinner';
9
9
  import TMCheckBox from '../editors/TMCheckBox';
10
10
  import TMPanel from '../base/TMPanel';
11
11
  import TMModal from '../base/TMModal';
12
+ import { useDataListItem } from '../../hooks/useDataListItem';
13
+ import { useDataUserIdItem } from '../../hooks/useDataUserIdItem';
12
14
  const StyledDistinctValues = styled.div `display: flex; flex-direction: column; height: 100%; overflow: hidden; gap: 10px;`;
13
- const StyledPanelContainer = styled.div ` width: 100%; height: 100%; padding: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; `;
14
15
  const TMDistinctValues = ({ tid, mid, layoutMode = LayoutModes.None, allowAppendMode = true, showHeader = true, isModal, separator = " ", onSelectionChanged, onClosePanelCallback }) => {
15
16
  const [focusedItem, setFocusedItem] = useState();
16
17
  const [dataSource, setDataSource] = useState([]);
17
18
  const [isAppendMode, setIsAppendMode] = useState(false);
18
19
  const [md, setMd] = useState();
19
20
  const [currentValue, setCurrentValue] = useState('');
21
+ const { loadDataListsAsync, renderDataListCell, dataListsCache } = useDataListItem();
22
+ const { loadUsersAsync, renderUserIdViewer, usersCache } = useDataUserIdItem();
20
23
  useEffect(() => {
21
24
  getDistictValuesAsync();
22
25
  }, [mid]);
@@ -57,8 +60,23 @@ const TMDistinctValues = ({ tid, mid, layoutMode = LayoutModes.None, allowAppend
57
60
  try {
58
61
  TMSpinner.show({ description: 'Caricamento dei valori distinti...' });
59
62
  let dtd = await DcmtTypeListCacheService.GetAsync(tid, true);
60
- setMd(dtd?.metadata?.find(o => o.id === mid));
63
+ const currentMd = dtd?.metadata?.find(o => o.id === mid);
64
+ setMd(currentMd);
61
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
+ }
62
80
  setDataSource(convertDataTableToObject(result?.dtdResult));
63
81
  }
64
82
  catch (e) {
@@ -69,31 +87,27 @@ const TMDistinctValues = ({ tid, mid, layoutMode = LayoutModes.None, allowAppend
69
87
  TMSpinner.hide();
70
88
  }
71
89
  };
72
- const getDcmtTypeAsync = async () => {
73
- if (!tid)
74
- return;
75
- try {
76
- TMSpinner.show({ description: 'Caricamento dei valori distinti...' });
77
- let dtd = await DcmtTypeListCacheService.GetAsync(tid, true);
78
- setMd(dtd?.metadata?.find(o => o.id === mid));
79
- }
80
- catch (e) {
81
- let err = e;
82
- TMExceptionBoxManager.show({ exception: err });
83
- }
84
- finally {
85
- TMSpinner.hide();
86
- }
87
- };
88
90
  const onFocusedRowChanged = useCallback((e) => {
89
91
  setFocusedItem(e.row?.data);
90
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]);
91
104
  const customColumns = useMemo(() => {
105
+ const needsCellRender = md?.dataDomain === MetadataDataDomains.DataList || md?.dataDomain === MetadataDataDomains.UserID;
92
106
  return ([
93
- { dataField: 'value', dataType: md?.dataType === MetadataDataTypes.DateTime ? 'date' : 'string', caption: md?.nameLoc, width: 'min-content' },
107
+ { dataField: 'value', dataType: md?.dataType === MetadataDataTypes.DateTime ? 'date' : 'string', caption: md?.nameLoc, width: 'min-content', ...(needsCellRender && { cellRender: valueCellRender }) },
94
108
  { dataField: 'Count', dataType: 'number', caption: 'N°' }
95
109
  ]);
96
- }, [md]);
110
+ }, [md, valueCellRender]);
97
111
  const customSummary = useMemo(() => {
98
112
  return ({
99
113
  totalItems: [
@@ -1,11 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useRef } from 'react';
2
+ import { useEffect, useId, useRef } from 'react';
3
3
  import { DateBox } from 'devextreme-react';
4
4
  import { DateDisplayTypes, Globalization } from '../../helper';
5
5
  import { TMColors } from '../../utils/theme';
6
6
  import TMVilViewer from '../base/TMVilViewer';
7
7
  const TMDateBox = (props) => {
8
8
  const resetTimeToZero = props.resetTimeToZeroOnKeyPress ?? true;
9
+ const autoId = useId();
10
+ const effectiveInputAttr = { id: autoId, ...props.inputAttr };
9
11
  useEffect(() => {
10
12
  let htmlElement = dateBoxRef?.current?.instance().element();
11
13
  if (!htmlElement)
@@ -63,7 +65,7 @@ const TMDateBox = (props) => {
63
65
  return "datetime";
64
66
  return props.dateDisplayType == DateDisplayTypes.Date ? "date" : "time";
65
67
  };
66
- return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', width: '100%', padding: props.padding }, children: [props.icon && (_jsx("span", { style: { marginRight: '8px', marginTop: '8px', display: 'flex', alignItems: 'center' }, children: props.icon })), _jsxs("div", { onContextMenu: (e) => e.stopPropagation(), style: { display: 'flex', flexDirection: 'column', gap: '5px', width: '100%' }, children: [_jsx(DateBox, { readOnly: props.readOnly, ref: dateBoxRef, showClearButton: props.showClearButton, dateSerializationFormat: props.useDateSerializationFormat ? 'yyyy-MM-ddTHH:mm:ss' : undefined, disabled: props.disabled, displayFormat: props.displayFormat ?? Globalization.getDateDisplayFormat(props.dateDisplayType), dropDownOptions: dropDownOptions, label: props.label, labelMode: 'static', type: getType(), useMaskBehavior: true, height: '28px', value: props.value, width: props.width, valueChangeEvent: 'keyup input change', onValueChange: (e) => { props.onValueChange?.(e); }, onInitialized: (e) => { props.onInitialized?.(e); }, onContentReady: (e) => { props.onContentReady?.(e); }, placeholder: props.placeholder, onKeyUp: (e) => {
68
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', width: '100%', padding: props.padding }, children: [props.icon && (_jsx("span", { style: { marginRight: '8px', marginTop: '8px', display: 'flex', alignItems: 'center' }, children: props.icon })), _jsxs("div", { onContextMenu: (e) => e.stopPropagation(), style: { display: 'flex', flexDirection: 'column', gap: '5px', width: '100%' }, children: [_jsx(DateBox, { readOnly: props.readOnly, ref: dateBoxRef, showClearButton: props.showClearButton, dateSerializationFormat: props.useDateSerializationFormat ? 'yyyy-MM-ddTHH:mm:ss' : undefined, disabled: props.disabled, displayFormat: props.displayFormat ?? Globalization.getDateDisplayFormat(props.dateDisplayType), dropDownOptions: dropDownOptions, label: props.label, labelMode: 'static', type: getType(), useMaskBehavior: true, height: '28px', value: props.value, width: props.width, valueChangeEvent: 'keyup input change', onValueChange: (e) => { props.onValueChange?.(e); }, onInitialized: (e) => { props.onInitialized?.(e); }, onContentReady: (e) => { props.onContentReady?.(e); }, placeholder: props.placeholder, ...(props.name && { name: props.name }), inputAttr: effectiveInputAttr, onKeyUp: (e) => {
67
69
  if (e.event?.code == "Space") {
68
70
  const currentDate = new Date();
69
71
  currentDate.setHours(0, 0, 0, 0);
@@ -27,11 +27,13 @@ export declare class FormulaDescriptor {
27
27
  mid?: number;
28
28
  metadataDataTypeDest?: MetadataDataTypes;
29
29
  tid?: number;
30
+ description?: string;
30
31
  }
31
32
  export declare const renderFormulaIcon: (iconType: FormulaIconTypes, tid?: number, md?: MetadataDescriptor) => import("react/jsx-runtime").JSX.Element;
32
33
  declare const TMFormulaEditor: React.FunctionComponent<ITMApplyFormProps<FormulaDescriptor>>;
33
34
  export default TMFormulaEditor;
34
35
  export declare class FormulaHelper {
36
+ static translateDescInFormula: (text: string, returnType?: string) => string;
35
37
  static isFormula(value: string | undefined): boolean;
36
38
  static removeFormulaTag(value: string | undefined): string | undefined;
37
39
  static addFormulaTag(value: string | undefined): string | undefined;