@topconsultnpm/sdkui-react 6.20.0-dev1.60 → 6.20.0-dev1.62

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.
@@ -1,8 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useState, forwardRef, useRef, useImperativeHandle } from 'react';
3
3
  import styled from 'styled-components';
4
- import ContextMenu from 'devextreme-react/context-menu';
5
- import { FontSize, TMColors } from '../../utils/theme';
4
+ import { ContextMenu as TMContextMenu } from '../NewComponents/ContextMenu';
5
+ import { TMColors } from '../../utils/theme';
6
6
  import { genUniqueId } from '../../helper';
7
7
  const StyledContent = styled.div `
8
8
  cursor: pointer;
@@ -22,14 +22,6 @@ const StyledContent = styled.div `
22
22
  border-bottom-color: ${TMColors.primary};
23
23
  }
24
24
  `;
25
- const StyledMenuItem = styled.div `
26
- display: flex;
27
- align-items: center;
28
- justify-content: space-between;
29
- gap: 6px;
30
- width: 100%;
31
- font-size: ${FontSize.defaultFontSize};
32
- `;
33
25
  const TMDropDownMenu = forwardRef(({ content, items, disabled = false, color = TMColors.text_normal, backgroundColor = TMColors.default_background, borderRadius, onMenuShown }, ref) => {
34
26
  const [id, setID] = useState('');
35
27
  const dropDownMenuElementRef = useRef(null); // Ref all'elemento DOM div principale
@@ -39,7 +31,32 @@ const TMDropDownMenu = forwardRef(({ content, items, disabled = false, color = T
39
31
  dropDownMenuElementRef.current?.focus();
40
32
  },
41
33
  }));
42
- const renderItemTemplate = (itemData) => (_jsxs(StyledMenuItem, { children: [itemData.icon && _jsx("div", { style: { display: 'flex', alignItems: 'center' }, children: itemData.icon }), _jsx("span", { style: { flexGrow: 1 }, children: itemData.text }), itemData.items && _jsx("span", { className: "dx-icon-spinright dx-icon", style: { marginLeft: '10px' } })] }));
43
- return (_jsxs(_Fragment, { children: [_jsx(StyledContent, { id: `idContainer${id}`, ref: dropDownMenuElementRef, tabIndex: disabled ? -1 : 0, "$disabled": disabled, "$color": color, "$backgroundColor": backgroundColor, "$borderRadius": borderRadius, children: content }), _jsx(ContextMenu, { target: `#idContainer${id}`, dataSource: items, showEvent: 'click', itemRender: renderItemTemplate, onShown: (e) => onMenuShown?.(), onHidden: (e) => dropDownMenuElementRef.current?.focus() })] }));
34
+ // Converter function: ITMDropDownMenuItem -> TMContextMenuItemProps
35
+ const convertToContextMenuItems = (dropDownItems) => {
36
+ if (!dropDownItems)
37
+ return [];
38
+ return dropDownItems.map(item => ({
39
+ name: item.text,
40
+ icon: item.icon,
41
+ disabled: item.disabled,
42
+ beginGroup: item.beginGroup,
43
+ onClick: item.onClick,
44
+ submenu: item.items ? convertToContextMenuItems(item.items) : undefined,
45
+ }));
46
+ };
47
+ const [menuVisible, setMenuVisible] = useState(false);
48
+ const handleMenuOpen = () => {
49
+ setMenuVisible(true);
50
+ onMenuShown?.();
51
+ };
52
+ const handleMenuClose = () => {
53
+ setMenuVisible(false);
54
+ dropDownMenuElementRef.current?.focus();
55
+ };
56
+ return (_jsxs(_Fragment, { children: [_jsx(StyledContent, { id: `idContainer${id}`, ref: dropDownMenuElementRef, tabIndex: disabled ? -1 : 0, "$disabled": disabled, "$color": color, "$backgroundColor": backgroundColor, "$borderRadius": borderRadius, onClick: !disabled ? handleMenuOpen : undefined, children: content }), _jsx(TMContextMenu, { items: convertToContextMenuItems(items), target: `#idContainer${id}`, trigger: "left", externalControl: {
57
+ visible: menuVisible,
58
+ position: { x: 0, y: 0 },
59
+ onClose: handleMenuClose,
60
+ } })] }));
44
61
  });
45
62
  export default TMDropDownMenu;
@@ -133,7 +133,7 @@ onActivate, onBack, onClose, onHeaderDoubleClick, onMaximize, onActiveChanged },
133
133
  };
134
134
  return (_jsxs(StyledPanelContainer, { ref: panelRef, "$isMaximized": onMaximize ? false : isMaximized, style: {
135
135
  visibility: isVisible ? 'visible' : 'hidden',
136
- }, tabIndex: -1, onFocus: !isControlled ? handleFocusUncontrolled : undefined, onBlur: !isControlled ? handleBlurUncontrolled : undefined, onClick: handleActivation, children: [showHeader &&
136
+ }, tabIndex: -1, onFocus: !isControlled ? handleFocusUncontrolled : undefined, onBlur: !isControlled ? handleBlurUncontrolled : undefined, onClick: handleActivation, onContextMenu: (e) => e.preventDefault(), children: [showHeader &&
137
137
  _jsx(StyledPanelHeader, { "$backgroundColor": backgroundColor, "$color": color, "$isActive": currentIsActive, onDoubleClick: () => {
138
138
  if (onHeaderDoubleClick)
139
139
  onHeaderDoubleClick();
@@ -30,11 +30,6 @@ interface ITMUserChooserFormProps extends ITMChooserFormProps<UserDescriptor> {
30
30
  allowShowAllUsers?: boolean;
31
31
  }
32
32
  export declare const TMUserChooserForm: React.FunctionComponent<ITMUserChooserFormProps>;
33
- export declare const TMUserIdViewer: ({ userId, showIcon, noneSelectionText }: {
34
- userId?: number;
35
- showIcon?: boolean;
36
- noneSelectionText?: string;
37
- }) => import("react/jsx-runtime").JSX.Element;
38
33
  export declare const TMUserIcon: ({ ud }: {
39
34
  ud?: UserDescriptor;
40
35
  }) => import("react/jsx-runtime").JSX.Element | null;
@@ -1,15 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { OwnershipLevels, SDK_Localizator, UserLevels, UserListCacheService } from '@topconsultnpm/sdk-ts';
4
- import { SDKUI_Localizator, IconSearch, IconUserLevelMember, IconUserLevelAdministrator, IconUserLevelSystemAdministrator, IconUserLevelAutonomousAdministrator, IconWarning, LocalizeOwnershipLevels, LocalizeUserLevels, IconShowAllUsersOff, IconShowAllUsers } from '../../helper';
5
- import { StyledDivHorizontal, StyledTooltipContainer, StyledTooltipSeparatorItem } from '../base/Styled';
4
+ import { SDKUI_Localizator, IconSearch, IconUserLevelMember, IconUserLevelAdministrator, IconUserLevelSystemAdministrator, IconUserLevelAutonomousAdministrator, LocalizeOwnershipLevels, LocalizeUserLevels, IconShowAllUsersOff, IconShowAllUsers } from '../../helper';
5
+ import { StyledDivHorizontal } from '../base/Styled';
6
6
  import TMSpinner from '../base/TMSpinner';
7
- import TMTooltip from '../base/TMTooltip';
8
7
  import TMSummary from '../editors/TMSummary';
9
8
  import TMChooserForm from '../forms/TMChooserForm';
10
- import { TMColors } from '../../utils/theme';
11
- import { TMExceptionBoxManager } from '../base/TMPopUp';
12
9
  import TMButton from '../base/TMButton';
10
+ import TMDataUserIdItemViewer from '../viewers/TMDataUserIdItemViewer';
13
11
  const TMUserChooser = ({ labelColor, titleForm, filter, readOnly = false, icon, width, dataSource, backgroundColor, openChooserBySingleClick, buttons = [], disabled = false, showBorder = true, hideRefresh = false, hideShowId = false, elementStyle, allowMultipleSelection, values, isModifiedWhen, label, placeHolder, validationItems = [], onValueChanged, showClearButton, initialShowChooser = false, allowShowAllUsers = false }) => {
14
12
  const [showChooser, setShowChooser] = useState(initialShowChooser);
15
13
  useEffect(() => {
@@ -18,7 +16,7 @@ const TMUserChooser = ({ labelColor, titleForm, filter, readOnly = false, icon,
18
16
  const summaryInputRef = useRef(null);
19
17
  const renderTemplate = () => {
20
18
  const isPlaceholder = values?.[0] === placeHolder;
21
- return (_jsxs(StyledDivHorizontal, { style: { minWidth: '125px', color: isPlaceholder ? '#a9a9a9' : 'inherit' }, children: [values && values.length > 0 && _jsx(TMUserIdViewer, { userId: values?.[0], showIcon: true, noneSelectionText: '' }), values && values.length > 1 && _jsx("p", { style: { marginLeft: '10px' }, children: `(+${values.length - 1} ${values.length == 2 ? 'altro' : 'altri'})` })] }));
19
+ return (_jsxs(StyledDivHorizontal, { style: { minWidth: '125px', color: isPlaceholder ? '#a9a9a9' : 'inherit' }, children: [values && values.length > 0 && _jsx(TMDataUserIdItemViewer, { userId: values?.[0], showIcon: true }), values && values.length > 1 && _jsx("p", { style: { marginLeft: '10px' }, children: `(+${values.length - 1} ${values.length == 2 ? 'altro' : 'altri'})` })] }));
22
20
  };
23
21
  return (_jsxs(_Fragment, { children: [_jsx(TMSummary, { ref: summaryInputRef, width: width, disabled: disabled, placeHolder: placeHolder, readOnly: readOnly, labelColor: labelColor, icon: icon, backgroundColor: backgroundColor, buttons: buttons, showBorder: showBorder, hasValue: values && values.length > 0, showClearButton: showClearButton, iconEditButton: _jsx(IconSearch, { fontSize: 16 }), onEditorClick: () => !readOnly && setShowChooser(true), elementStyle: elementStyle, isModifiedWhen: isModifiedWhen, openEditorOnSummaryClick: openChooserBySingleClick, label: label, template: renderTemplate(), onClearClick: showClearButton ? () => { onValueChanged?.([]); } : undefined, validationItems: validationItems }), showChooser &&
24
22
  _jsx(TMUserChooserForm, { title: titleForm, allowMultipleSelection: allowMultipleSelection, hasShowOnlySelectedItems: true, dataSource: dataSource, filter: filter, selectedIDs: values, hideRefresh: hideRefresh, hideShowId: hideShowId, allowShowAllUsers: allowShowAllUsers, onClose: () => {
@@ -67,40 +65,6 @@ export const TMUserChooserForm = ({ allowMultipleSelection, columns, hideRefresh
67
65
  const customButton = (allowShowAllUsers && dataSource) ? (_jsx(TMButton, { btnStyle: 'toolbar', caption: showingAllUsers ? SDKUI_Localizator.HideAll : SDKUI_Localizator.ShowAll, onClick: handleToggleAllUsers, icon: showingAllUsers ? _jsx(IconShowAllUsersOff, {}) : _jsx(IconShowAllUsers, {}) })) : undefined;
68
66
  return (_jsx(TMChooserForm, { title: getTitle(), allowMultipleSelection: allowMultipleSelection, startWithShowOnlySelectedItems: startWithShowOnlySelectedItems, hasShowOnlySelectedItems: hasShowOnlySelectedItems, width: width, height: height, manageUseLocalizedName: false, columns: columns ?? dataColumns, showDefaultColumns: false, selectedIDs: selectedIDs, cellRenderIcon: cellRenderIcon, dataSource: currentDataSource, getItems: getItems, hasShowId: !hideShowId, hideRefresh: hideRefresh, customButtons: customButton, onClose: onClose, onChoose: (IDs) => onChoose?.(IDs) }));
69
67
  };
70
- export const TMUserIdViewer = ({ userId, showIcon = false, noneSelectionText = `<${SDKUI_Localizator.NoneSelection}>` }) => {
71
- const [ud, setUd] = useState();
72
- useEffect(() => {
73
- if (!userId || userId <= 0) {
74
- setUd(undefined);
75
- return;
76
- }
77
- TMSpinner.show({ description: `${SDKUI_Localizator.Loading} - ${SDK_Localizator.Users} ...` });
78
- UserListCacheService.GetAsync(userId).then((ud) => { setUd(ud); TMSpinner.hide(); }).catch((err) => { TMExceptionBoxManager.show({ exception: err }); });
79
- }, [userId]);
80
- const getIcon = () => {
81
- if (!showIcon)
82
- return null;
83
- if (!userId)
84
- return null;
85
- return ud ?
86
- _jsx(TMUserIcon, { ud: ud })
87
- :
88
- _jsx(TMTooltip, { content: SDKUI_Localizator.ValueNotPresent, children: _jsx(IconWarning, { color: TMColors.warning }) });
89
- };
90
- const getDescription = () => {
91
- if (!userId)
92
- return undefined;
93
- return ud ? getCompleteUserName(ud.domain, ud.name) : userId.toString() ?? noneSelectionText;
94
- };
95
- return (_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '4px' }, children: [getIcon(), _jsx("span", { children: getDescription() })] })
96
- // <StyledDivHorizontal>
97
- // {getIcon()}
98
- // <p style={{ textAlign: 'left', marginLeft: showIcon ? '5px' : '', opacity: ud ? 1 : 0.5 }}>
99
- // {getDescription()}
100
- // </p>
101
- // </StyledDivHorizontal>
102
- );
103
- };
104
68
  export const TMUserIcon = ({ ud }) => {
105
69
  if (!ud)
106
70
  return null;
@@ -109,12 +73,28 @@ export const TMUserIcon = ({ ud }) => {
109
73
  return (_jsx(TMUserTooltip, { ud: ud, children: icon }));
110
74
  };
111
75
  export const TMUserTooltip = ({ ud, children }) => {
112
- const renderTooltipContent = (ud) => {
113
- let isExpired = User_IsExpired(ud);
114
- return (!ud ? null
115
- : _jsxs(StyledTooltipContainer, { children: [_jsx("div", { children: `ID: ${ud.id}` }), _jsx("div", { children: `${SDKUI_Localizator.Name}: ${getCompleteUserName(ud.domain, ud.name)}` }), ud.description && _jsx("div", { children: `${SDKUI_Localizator.Description}: ${ud.description}` }), ud.type && _jsxs("div", { children: [SDKUI_Localizator.Type, ": ", ud.type] }), ud.level && _jsxs("div", { children: [SDKUI_Localizator.UserLevel, ": ", LocalizeUserLevels(ud.level)] }), _jsxs("div", { children: [SDKUI_Localizator.Valid, ": ", isExpired ? SDKUI_Localizator.No : SDKUI_Localizator.Yes, " - ", SDKUI_Localizator.Disabled, ": ", ud.disabled ? SDKUI_Localizator.Yes : SDKUI_Localizator.No] }), _jsx(StyledTooltipSeparatorItem, {}), _jsxs("div", { children: [SDKUI_Localizator.OwnerName, ": ", ud.ownerName, " (", ud.ownerID, ")"] }), _jsxs("div", { children: [SDKUI_Localizator.OwnershipLevel, ": ", LocalizeOwnershipLevels(ud.ownershipLevel)] }), _jsxs("div", { children: [SDKUI_Localizator.CreationTime, ": ", ud.creationTime?.toDateString()] }), _jsxs("div", { children: [SDKUI_Localizator.LastUpdateTime, ": ", ud.lastUpdateTime?.toDateString()] })] }));
76
+ const buildTitle = (ud) => {
77
+ if (!ud)
78
+ return '';
79
+ const isExpired = User_IsExpired(ud);
80
+ return [
81
+ `ID: ${ud.id ?? ''}`,
82
+ `${SDKUI_Localizator.Name}: ${getCompleteUserName(ud.domain, ud.name) ?? ''}`,
83
+ ud.description && `${SDKUI_Localizator.Description}: ${ud.description}`,
84
+ ud.type && `${SDKUI_Localizator.Type}: ${ud.type}`,
85
+ ud.level && `${SDKUI_Localizator.UserLevel}: ${LocalizeUserLevels(ud.level) ?? ''}`,
86
+ `${SDKUI_Localizator.Valid}: ${isExpired ? SDKUI_Localizator.No : SDKUI_Localizator.Yes}`,
87
+ `${SDKUI_Localizator.Disabled}: ${ud.disabled ? SDKUI_Localizator.Yes : SDKUI_Localizator.No}`,
88
+ `---`,
89
+ `${SDKUI_Localizator.OwnerName}: ${ud.ownerName ?? ''} (${ud.ownerID ?? ''})`,
90
+ `${SDKUI_Localizator.OwnershipLevel}: ${LocalizeOwnershipLevels(ud.ownershipLevel) ?? ''}`,
91
+ `${SDKUI_Localizator.CreationTime}: ${ud.creationTime?.toDateString() ?? ''}`,
92
+ `${SDKUI_Localizator.LastUpdateTime}: ${ud.lastUpdateTime?.toDateString() ?? ''}`
93
+ ]
94
+ .filter(Boolean)
95
+ .join('\n');
116
96
  };
117
- return (_jsx(TMTooltip, { content: renderTooltipContent(ud), children: children }));
97
+ return (_jsx("div", { title: buildTitle(ud), children: children }));
118
98
  };
119
99
  const getCompleteUserName = (domain, name) => {
120
100
  if (!name)
@@ -65,7 +65,7 @@ export interface TMRelationViewerProps {
65
65
  /**
66
66
  * Show metadata names before values (default: false).
67
67
  * When true, displays "MetadataName: Value", otherwise just "Value".
68
- * Value rendering respects DataDomain (uses TMDataListItemViewer for lists, TMUserIdViewer for users, etc.)
68
+ * Value rendering respects DataDomain (uses TMDataListItemViewer for lists, TMDataUserIdItemViewer for users, etc.)
69
69
  */
70
70
  showMetadataNames?: boolean;
71
71
  /** Maximum depth level for recursive loading (default: 2) */
@@ -6,9 +6,9 @@ import { TMColors } from '../../../utils/theme';
6
6
  import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
7
7
  import TMTreeView from '../../base/TMTreeView';
8
8
  import { TMWaitPanel } from '../../base/TMWaitPanel';
9
- import { TMUserIdViewer } from '../../choosers/TMUserChooser';
10
9
  import TMDataListItemViewer from '../../viewers/TMDataListItemViewer';
11
10
  import TMDcmtIcon from './TMDcmtIcon';
11
+ import TMDataUserIdItemViewer from '../../viewers/TMDataUserIdItemViewer';
12
12
  /**
13
13
  * Check if document type has detail relations
14
14
  */
@@ -685,7 +685,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
685
685
  flexShrink: isLast ? 1 : 0,
686
686
  minWidth: isLast ? 0 : 'auto',
687
687
  overflow: isLast ? 'hidden' : 'visible'
688
- }, children: [index > 0 && _jsx("span", { style: { margin: '0 5px', color: '#999' }, children: "\u2022" }), showMetadataNames && (_jsxs("span", { style: { color: '#666', marginRight: '5px' }, children: [md?.name || key, ":"] })), md?.dataDomain === MetadataDataDomains.DataList ? (_jsx(TMDataListItemViewer, { dataListId: md.dataListID, viewMode: md.dataListViewMode, value: value })) : md?.dataDomain === MetadataDataDomains.UserID ? (_jsx(TMUserIdViewer, { userId: value, showIcon: true, noneSelectionText: '' })) : (_jsx("span", { style: {
688
+ }, children: [index > 0 && _jsx("span", { style: { margin: '0 5px', color: '#999' }, children: "\u2022" }), showMetadataNames && (_jsxs("span", { style: { color: '#666', marginRight: '5px' }, children: [md?.name || key, ":"] })), md?.dataDomain === MetadataDataDomains.DataList ? (_jsx(TMDataListItemViewer, { dataListId: md.dataListID, viewMode: md.dataListViewMode, value: value })) : md?.dataDomain === MetadataDataDomains.UserID ? (_jsx(TMDataUserIdItemViewer, { userId: value, showIcon: true })) : (_jsx("span", { style: {
689
689
  fontWeight: 500,
690
690
  overflow: isLast ? 'hidden' : 'visible',
691
691
  textOverflow: isLast ? 'ellipsis' : 'clip',
@@ -16,9 +16,7 @@ import { useDeviceType, DeviceType } from '../../base/TMDeviceProvider';
16
16
  import { TMSplitterLayout, TMLayoutItem } from '../../base/TMLayout';
17
17
  import { TMMessageBoxManager, ButtonNames, TMExceptionBoxManager } from '../../base/TMPopUp';
18
18
  import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
19
- import { TMUserIdViewer } from '../../choosers/TMUserChooser';
20
19
  import TMMetadataValues from '../../editors/TMMetadataValues';
21
- import TMDataListItemViewer from '../../viewers/TMDataListItemViewer';
22
20
  import TMTidViewer from '../../viewers/TMTidViewer';
23
21
  import TMDcmtPreview from '../documents/TMDcmtPreview';
24
22
  import TMFloatingMenuBar from '../../NewComponents/FloatingMenuBar/TMFloatingMenuBar';
@@ -48,6 +46,8 @@ import TMViewHistoryDcmt from './TMViewHistoryDcmt';
48
46
  import TMBlogCommentForm from '../blog/TMBlogCommentForm';
49
47
  import { useCheckInOutOperations } from '../../../hooks/useCheckInOutOperations';
50
48
  import TMDcmtCheckoutInfoForm from './TMDcmtCheckoutInfoForm';
49
+ import { useDataListItem } from '../../../hooks/useDataListItem';
50
+ import { useDataUserIdItem } from '../../../hooks/useDataUserIdItem';
51
51
  let abortControllerLocal = new AbortController();
52
52
  //#region Helper Methods
53
53
  export const getSearchResultCountersSingleCategory = (searchResults) => {
@@ -593,7 +593,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
593
593
  currentTIDHasDetailRelations, canArchiveMasterRelation, canArchiveDetailRelation,
594
594
  hasManyToManyRelation, customButtonsLayout
595
595
  ]);
596
- const searchResutlToolbar = _jsxs(_Fragment, { children: [(dcmtsReturned != dcmtsFound) && _jsx("p", { style: { backgroundColor: `white`, color: TMColors.primaryColor, textAlign: 'center', padding: '1px 4px', borderRadius: '3px', display: 'flex' }, children: `${dcmtsReturned}/${dcmtsFound} restituiti` }), context === SearchResultContext.FAVORITES_AND_RECENTS &&
596
+ const searchResutlToolbar = _jsxs(_Fragment, { children: [(dcmtsReturned != dcmtsFound) && _jsx("p", { style: { textAlign: 'center', padding: '1px 4px', borderRadius: '3px', display: 'flex' }, children: `${dcmtsReturned}/${dcmtsFound} restituiti` }), context === SearchResultContext.FAVORITES_AND_RECENTS &&
597
597
  _jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: onRefreshSearchAsync }), _jsx(TMContextMenu, { items: floatingMenuItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) })] });
598
598
  const tmSearchResult = useMemo(() => (!searchResults || searchResults.length <= 0)
599
599
  ? _jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%' }, children: [_jsx(IconBoard, { fontSize: 96 }), _jsx("div", { style: { fontSize: "15px", marginTop: "10px" }, children: SDKUI_Localizator.NoDcmtFound }), openAddDocumentForm && _jsx("div", { style: { marginTop: "10px" }, children: _jsx(TMButton, { fontSize: "15px", icon: _jsx("i", { className: 'dx-icon-share' }), caption: SDKUI_Localizator.Share, onClick: openAddDocumentForm }) })] })
@@ -805,6 +805,8 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
805
805
  const [selectedRowKeys, setSelectedRowKeys] = useState([]);
806
806
  const [focusedItem, setFocusedItem] = useState();
807
807
  const [visibleItems, setVisibleItems] = useState([]);
808
+ const { loadDataListsAsync, renderDataListCell } = useDataListItem();
809
+ const { loadUsersAsync, renderUserIdViewer } = useDataUserIdItem();
808
810
  useEffect(() => {
809
811
  if (deepCompare(inputFocusedItem, focusedItem))
810
812
  return;
@@ -894,10 +896,10 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
894
896
  }
895
897
  let child = _jsx("div", { children: cellData.text });
896
898
  if (dataDomain === MetadataDataDomains.DataList) {
897
- child = _jsx(TMDataListItemViewer, { dataListId: dataListID, viewMode: dataListViewMode, value: cellData.value });
899
+ child = renderDataListCell(cellData.value, dataListID, dataListViewMode);
898
900
  }
899
901
  if (dataDomain === MetadataDataDomains.UserID) {
900
- child = _jsx(TMUserIdViewer, { userId: cellData.value, showIcon: true, noneSelectionText: '' });
902
+ child = renderUserIdViewer(cellData.value, true);
901
903
  }
902
904
  return (_jsxs("div", { style: style, children: [shouldShowCheckoutIcon && checkoutStatus.icon, child] }));
903
905
  }, [fromDTD, allUsers]);
@@ -953,29 +955,55 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
953
955
  useEffect(() => {
954
956
  if (fromDTD === undefined || searchResult === undefined)
955
957
  return;
956
- setFocusedItem(undefined); // resetta sempre prima
957
- let cols = [];
958
- // Generate unique keys for all columns
959
- const uniqueKeys = generateUniqueColumnKeys(searchResult?.dtdResult?.columns, searchResult?.fromTID);
960
- searchResult?.dtdResult?.columns?.map((col, index) => {
961
- const isVisible = col.extendedProperties?.["Visibility"] != "Hidden";
962
- const dataDomain = MetadataDataDomains[(col.extendedProperties?.["DataDomain"] ?? "None")];
963
- const dataListID = Number(col.extendedProperties?.["DataListID"]);
964
- const dataListViewMode = DataListViewModes[(col.extendedProperties?.["DataListViewMode"] ?? "None")];
965
- cols.push({
966
- dataField: uniqueKeys[index],
967
- dataType: dataType(col),
968
- visible: isVisible,
969
- cellRender: (cellData) => cellRender(cellData, dataDomain, dataListID, dataListViewMode),
970
- caption: col.caption,
971
- format: getDisplayFormat(col),
958
+ const loadColumnsAndData = async () => {
959
+ setFocusedItem(undefined); // resetta sempre prima
960
+ let cols = [];
961
+ // Generate unique keys for all columns
962
+ const uniqueKeys = generateUniqueColumnKeys(searchResult?.dtdResult?.columns, searchResult?.fromTID);
963
+ const dataListIDs = new Set();
964
+ const userIDs = new Set();
965
+ searchResult?.dtdResult?.columns?.forEach((col) => {
966
+ const dataDomain = MetadataDataDomains[(col.extendedProperties?.["DataDomain"] ?? "None")];
967
+ const dataListID = Number(col.extendedProperties?.["DataListID"]);
968
+ if (dataDomain === MetadataDataDomains.DataList && dataListID) {
969
+ dataListIDs.add(dataListID);
970
+ }
972
971
  });
973
- });
974
- setColumns(cols);
975
- let newDataSource = searchResultDescriptorToSimpleArray(searchResult);
976
- setDataSource(newDataSource);
977
- // setFocusedItem(newDataSource && newDataSource.length > 0 ? newDataSource[0] : undefined);
978
- }, [searchResult, fromDTD, allUsers]);
972
+ await loadDataListsAsync(dataListIDs);
973
+ // Carica gli UserID dalle righe
974
+ searchResult?.dtdResult?.rows?.forEach((row) => {
975
+ searchResult?.dtdResult?.columns?.forEach((col, colIndex) => {
976
+ const dataDomain = MetadataDataDomains[(col.extendedProperties?.["DataDomain"] ?? "None")];
977
+ if (dataDomain === MetadataDataDomains.UserID) {
978
+ const userId = Number(row[colIndex]);
979
+ if (userId && userId > 0) {
980
+ userIDs.add(userId);
981
+ }
982
+ }
983
+ });
984
+ });
985
+ await loadUsersAsync(userIDs);
986
+ searchResult?.dtdResult?.columns?.map((col, index) => {
987
+ const isVisible = col.extendedProperties?.["Visibility"] != "Hidden";
988
+ const dataDomain = MetadataDataDomains[(col.extendedProperties?.["DataDomain"] ?? "None")];
989
+ const dataListID = Number(col.extendedProperties?.["DataListID"]);
990
+ const dataListViewMode = DataListViewModes[(col.extendedProperties?.["DataListViewMode"] ?? "None")];
991
+ cols.push({
992
+ dataField: uniqueKeys[index],
993
+ dataType: dataType(col),
994
+ visible: isVisible,
995
+ cellRender: (cellData) => cellRender(cellData, dataDomain, dataListID, dataListViewMode),
996
+ caption: col.caption,
997
+ format: getDisplayFormat(col),
998
+ });
999
+ });
1000
+ setColumns(cols);
1001
+ let newDataSource = searchResultDescriptorToSimpleArray(searchResult);
1002
+ setDataSource(newDataSource);
1003
+ // setFocusedItem(newDataSource && newDataSource.length > 0 ? newDataSource[0] : undefined);
1004
+ };
1005
+ loadColumnsAndData();
1006
+ }, [searchResult, fromDTD, allUsers, loadDataListsAsync]);
979
1007
  useEffect(() => {
980
1008
  let newDataSource = searchResultDescriptorToSimpleArray(searchResult);
981
1009
  setDataSource(newDataSource);
@@ -1047,7 +1075,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, allUsers, inputFocusedItem,
1047
1075
  setVisibleItems(visibleRows.map((row) => { return row.data; }));
1048
1076
  }, []);
1049
1077
  useEffect(() => { onVisibleItemChanged?.(visibleItems); }, [visibleItems]);
1050
- return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx(TMDataGrid, { id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearch, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: TMDataGridPageSize.Small, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: floatingMenuItems, counterConfig: { show: true } }), (showExportForm && searchResult && onCloseExportForm) && _jsx(TMDataGridExportForm, { dataColumns: dataColumns, dataSource: dataSource, selectedRowKeys: selectedRowKeys, onCloseExportForm: onCloseExportForm, searchResult: searchResult })] });
1078
+ return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx(TMDataGrid, { id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearch, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: TMDataGridPageSize.Large, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: floatingMenuItems, counterConfig: { show: true } }), (showExportForm && searchResult && onCloseExportForm) && _jsx(TMDataGridExportForm, { dataColumns: dataColumns, dataSource: dataSource, selectedRowKeys: selectedRowKeys, onCloseExportForm: onCloseExportForm, searchResult: searchResult })] });
1051
1079
  };
1052
1080
  //#region TMSearchResultSelector
1053
1081
  const StyledItemTemplate = styled.div `
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'react';
3
3
  import styled from 'styled-components';
4
4
  import TMButton from '../../../base/TMButton';
5
5
  import { IconAdd, IconDelete, SDKUI_Localizator } from '../../../../helper';
6
- import { TMUserChooserForm, TMUserIdViewer } from '../../../choosers/TMUserChooser';
6
+ import { TMUserChooserForm } from '../../../choosers/TMUserChooser';
7
7
  import { TMGroupChooserForm, TMGroupIdViewer } from '../../../choosers/TMGroupChooser';
8
8
  import { TMMidViewer } from '../../../viewers/TMMidViewer';
9
9
  import { SDK_Globals, SDK_Localizator } from '@topconsultnpm/sdk-ts';
@@ -13,6 +13,7 @@ import { TMMessageBoxManager, ButtonNames } from '../../../base/TMPopUp';
13
13
  import TMModal from '../../../base/TMModal';
14
14
  import { TMMetadataChooserForm } from '../../../choosers/TMMetadataChooser';
15
15
  import TMQueryEditor from '../../../query/TMQueryEditor';
16
+ import TMDataUserIdItemViewer from '../../../viewers/TMDataUserIdItemViewer';
16
17
  export var WorkItemActorTypes;
17
18
  (function (WorkItemActorTypes) {
18
19
  WorkItemActorTypes[WorkItemActorTypes["None"] = 0] = "None";
@@ -168,7 +169,7 @@ const RecipientList = ({ recipients, title, tid, qd, onAdd, onRemove, onQDChange
168
169
  const actorId = parseInt(recipient.ActorID, 10);
169
170
  switch (recipient.ActorType) {
170
171
  case WorkItemActorTypes.UID:
171
- return _jsx(TMUserIdViewer, { userId: actorId, showIcon: true });
172
+ return _jsx(TMDataUserIdItemViewer, { userId: actorId, showIcon: true });
172
173
  case WorkItemActorTypes.GID:
173
174
  return _jsx(TMGroupIdViewer, { groupID: actorId, showIcon: true });
174
175
  case WorkItemActorTypes.MID:
@@ -1,9 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import styled from 'styled-components';
3
3
  import { useCallback, useEffect, useState } from 'react';
4
- import ReactDOMServer from 'react-dom/server';
5
4
  import { DcmtTypeListCacheService } from '@topconsultnpm/sdk-ts';
6
- import ContextMenu from 'devextreme-react/context-menu';
5
+ import { ContextMenu as TMContextMenu } from '../NewComponents/ContextMenu';
7
6
  import { IconDelete, SDKUI_Localizator, IconApply, IconInfo, IconCloseOutline } from '../../helper';
8
7
  import { TMColors } from '../../utils/theme';
9
8
  import { DeviceType } from '../base/TMDeviceProvider';
@@ -62,11 +61,15 @@ const StyledRecentTidItem = styled.div `
62
61
  margin-bottom: 0;
63
62
  }
64
63
  `;
65
- const iconDelete = () => ReactDOMServer.renderToString(_jsx(IconDelete, {}));
66
64
  const TMRecentsManager = ({ deviceType, mruTIDs, currentMruTID, accessFilter = 'all', onSelectedTID, onDeletedTID }) => {
67
65
  const [showDcmtTypeChooser, setShowDcmtTypeChooser] = useState(false);
68
66
  const [recentDcmtTypes, setRecentDcmtTypes] = useState([]);
69
67
  const [infoDTD, setInfoDTD] = useState();
68
+ const [contextMenuState, setContextMenuState] = useState({
69
+ visible: false,
70
+ position: { x: 0, y: 0 },
71
+ tid: null
72
+ });
70
73
  // Handler for the cache refresh event
71
74
  const handleCacheRefresh = useCallback(async () => {
72
75
  // Retrieve all document types without metadata from the refreshed cache
@@ -114,7 +117,10 @@ const TMRecentsManager = ({ deviceType, mruTIDs, currentMruTID, accessFilter = '
114
117
  textOverflow: 'ellipsis'
115
118
  }, children: `${SDKUI_Localizator.AllDcmtTypes} (${DcmtTypeListCacheService.CacheCount(true, accessFilter)})` }) }) }, 0), recentDcmtTypes.map((dtd) => {
116
119
  const isCurrent = currentMruTID == dtd.id;
117
- return (_jsxs(StyledRecentTidItem, { id: `tid-${dtd.id}`, "$isMobile": isMobile, onClick: () => { onSelectedTID?.(dtd.id ?? 0); }, children: [_jsxs(StyledDivHorizontal, { style: { alignItems: 'center', gap: 8, width: '100%' }, children: [!isMobile && (_jsx("span", { className: "info-icon", style: {
120
+ return (_jsxs(StyledRecentTidItem, { id: `tid-${dtd.id}`, "$isMobile": isMobile, onClick: () => { onSelectedTID?.(dtd.id ?? 0); }, onContextMenu: (e) => {
121
+ e.preventDefault();
122
+ setContextMenuState({ visible: true, position: { x: e.clientX, y: e.clientY }, tid: dtd.id ?? null });
123
+ }, children: [_jsxs(StyledDivHorizontal, { style: { alignItems: 'center', gap: 8, width: '100%' }, children: [!isMobile && (_jsx("span", { className: "info-icon", style: {
118
124
  marginRight: 4,
119
125
  display: 'flex',
120
126
  alignItems: 'center'
@@ -135,20 +141,24 @@ const TMRecentsManager = ({ deviceType, mruTIDs, currentMruTID, accessFilter = '
135
141
  fontWeight: 'bold',
136
142
  marginLeft: 8,
137
143
  visibility: isCurrent ? 'visible' : 'hidden'
138
- }, children: _jsx(IconApply, { fontSize: 24, color: 'green' }) })] }), _jsx(ContextMenu, { dataSource: [
144
+ }, children: _jsx(IconApply, { fontSize: 24, color: 'green' }) })] }), contextMenuState.tid === dtd.id && (_jsx(TMContextMenu, { items: [
139
145
  {
140
- text: SDKUI_Localizator.Remove,
141
- icon: iconDelete(),
146
+ name: SDKUI_Localizator.Remove,
147
+ icon: _jsx(IconDelete, {}),
142
148
  onClick: () => { onDeletedTID?.(dtd.id ?? 0); }
143
149
  },
144
150
  ...(isMobile ? [
145
151
  {
146
- text: SDKUI_Localizator.About,
147
- icon: ReactDOMServer.renderToString(_jsx(IconInfo, { color: TMColors.info })),
152
+ name: SDKUI_Localizator.About,
153
+ icon: _jsx(IconInfo, { color: TMColors.info }),
148
154
  onClick: () => { setInfoDTD(dtd); }
149
155
  }
150
156
  ] : [])
151
- ], target: `#tid-${dtd.id}` })] }, dtd.id));
157
+ ], target: `#tid-${dtd.id}`, externalControl: {
158
+ visible: contextMenuState.visible,
159
+ position: contextMenuState.position,
160
+ onClose: () => setContextMenuState({ visible: false, position: { x: 0, y: 0 }, tid: null })
161
+ } }))] }, dtd.id));
152
162
  })] }), showDcmtTypeChooser &&
153
163
  _jsx(TMDcmtTypeChooserForm, { accessFilter: accessFilter, onClose: () => setShowDcmtTypeChooser(false), onChoose: (tids) => { onSelectedTID?.(tids?.[0] ?? 0); } }), _jsxs(StyledOffCanvasPanel, { ref: panelRef, "$isOpen": isMobile && infoDTD !== undefined, children: [_jsxs(StyledDivHorizontal, { style: { gap: 10, padding: '10px 8px', width: '100%', alignItems: 'center' }, children: [_jsx("p", { style: { fontSize: '1.1rem', fontWeight: 'bold' }, children: `${SDKUI_Localizator.DcmtType} - ${SDKUI_Localizator.About}` }), _jsx(IconCloseOutline, { style: { marginLeft: 'auto', cursor: 'pointer' }, onClick: () => setInfoDTD(undefined) })] }), renderDTDTooltipContent(infoDTD)] })] }));
154
164
  };
@@ -16,6 +16,7 @@ export * from './base/TMTreeView';
16
16
  export * from './base/TMPanel';
17
17
  export * from './base/TMResizableMenu';
18
18
  export * from './base/TMAccordionNew';
19
+ export * from './NewComponents/ContextMenu';
19
20
  export { default as CounterBar } from './base/TMCounterBar';
20
21
  export { default as TMProgressBar } from './base/TMProgressBar';
21
22
  export { default as TMSpinner } from './base/TMSpinner';
@@ -92,6 +93,7 @@ export { default as SettingsAppearance } from "./settings/SettingsAppearance";
92
93
  export * from "./viewers/TMTidViewer";
93
94
  export * from "./viewers/TMMidViewer";
94
95
  export * from "./viewers/TMDataListItemViewer";
96
+ export * from "./viewers/TMDataUserIdItemViewer";
95
97
  export * from "./base/TMDeviceProvider";
96
98
  export { default as TMDataGrid } from "./base/TMDataGrid";
97
99
  export { default as TMFileManager } from "./base/TMFileManager";
@@ -17,6 +17,7 @@ export * from './base/TMTreeView';
17
17
  export * from './base/TMPanel';
18
18
  export * from './base/TMResizableMenu';
19
19
  export * from './base/TMAccordionNew';
20
+ export * from './NewComponents/ContextMenu';
20
21
  export { default as CounterBar } from './base/TMCounterBar';
21
22
  export { default as TMProgressBar } from './base/TMProgressBar';
22
23
  export { default as TMSpinner } from './base/TMSpinner';
@@ -110,6 +111,7 @@ export { default as SettingsAppearance } from "./settings/SettingsAppearance";
110
111
  export * from "./viewers/TMTidViewer";
111
112
  export * from "./viewers/TMMidViewer";
112
113
  export * from "./viewers/TMDataListItemViewer";
114
+ export * from "./viewers/TMDataUserIdItemViewer";
113
115
  //TMDeviceProvider
114
116
  export * from "./base/TMDeviceProvider";
115
117
  export { default as TMDataGrid } from "./base/TMDataGrid";
@@ -6,6 +6,6 @@ interface ITMDataListItemViewerProps {
6
6
  viewMode?: DataListViewModes;
7
7
  showTooltip?: boolean;
8
8
  }
9
- declare const TMDataListItemViewer: ({ dataListId, value, viewMode, showTooltip }: ITMDataListItemViewerProps) => import("react/jsx-runtime").JSX.Element;
9
+ declare const TMDataListItemViewer: ({ dataListId, value, viewMode, showTooltip }: ITMDataListItemViewerProps) => import("react/jsx-runtime").JSX.Element | null;
10
10
  export default TMDataListItemViewer;
11
11
  export declare const cellRenderDataListItem: (data: DataGridTypes.ColumnCellTemplateData, dataListId?: number, viewMode?: DataListViewModes) => import("react/jsx-runtime").JSX.Element;
@@ -1,78 +1,41 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
- import { DataListCacheService, DataListViewModes } from '@topconsultnpm/sdk-ts';
4
- import { IconWarning, SDKUI_Localizator, TMImageLibrary } from '../../helper';
5
- import { StyledDivHorizontal } from '../base/Styled';
3
+ import { DataListViewModes } from '@topconsultnpm/sdk-ts';
4
+ import { SDKUI_Localizator } from '../../helper';
6
5
  import TMTooltip from '../base/TMTooltip';
7
- import { TMColors } from '../../utils/theme';
8
6
  import { TMExceptionBoxManager } from '../base/TMPopUp';
9
- import { FormulaHelper } from '../editors/TMFormulaEditor';
7
+ import { useDataListItem } from '../../hooks/useDataListItem';
10
8
  const TMDataListItemViewer = ({ dataListId, value, viewMode = DataListViewModes.ImageAndDescription, showTooltip = true }) => {
11
- const [dataListItem, setDataListItem] = useState();
9
+ const { loadDataListsAsync, getDataListItem, renderDataListCell, convertToDataListValue } = useDataListItem();
10
+ const [dataListItem, setDataListItem] = useState(undefined);
12
11
  useEffect(() => {
13
- if (!dataListId || value === undefined || value === null) {
14
- setDataListItem(undefined);
15
- return;
16
- }
17
- let stringValue;
18
- if (value instanceof Date) {
19
- stringValue = value.toISOString();
20
- }
21
- else if (typeof value === 'number') {
22
- stringValue = value.toString();
23
- }
24
- else {
25
- stringValue = value;
26
- }
27
- DataListCacheService.GetAsync(dataListId).then((dl) => { setDataListItem(dl?.items?.find(o => o.value == stringValue)); }).catch((err) => { TMExceptionBoxManager.show({ exception: err }); });
28
- }, [dataListId, value]);
29
- let showIcon = viewMode != DataListViewModes.Description;
30
- const getIcon = () => {
31
- if (!showIcon)
32
- return null;
33
- if (value === undefined || value === null)
34
- return null;
35
- let stringValue;
36
- if (value instanceof Date) {
37
- stringValue = value.toISOString();
38
- }
39
- else if (typeof value === 'number') {
40
- stringValue = value.toString();
41
- }
42
- else {
43
- stringValue = value;
44
- }
45
- if (FormulaHelper.isFormula(stringValue))
46
- return null;
47
- return dataListItem ? _jsx(TMImageLibrary, { imageID: dataListItem.imageID }) : _jsx(IconWarning, { color: TMColors.warning });
48
- };
49
- const getDescription = () => {
50
- if (!dataListId)
51
- return undefined;
52
- if (value === undefined || value === null)
53
- return undefined;
54
- let displayValue;
55
- if (value instanceof Date) {
56
- displayValue = value.toLocaleDateString();
57
- }
58
- else if (typeof value === 'number') {
59
- displayValue = value.toString();
60
- }
61
- else {
62
- displayValue = value;
63
- }
64
- return dataListItem ? dataListItem.name : displayValue;
65
- };
66
- const content = (_jsxs(StyledDivHorizontal, { style: { width: '100%' }, children: [getIcon(), _jsx("p", { style: {
67
- textAlign: 'left',
68
- marginLeft: showIcon ? '5px' : '',
69
- opacity: dataListItem ? 1 : 0.5,
70
- whiteSpace: 'nowrap',
71
- overflow: 'hidden',
72
- textOverflow: 'ellipsis',
73
- flexGrow: 1,
74
- minWidth: 0
75
- }, children: getDescription() })] }));
12
+ const loadData = async () => {
13
+ if (dataListId) {
14
+ try {
15
+ await loadDataListsAsync(new Set([dataListId]));
16
+ if (value !== undefined && value !== null) {
17
+ const stringValue = convertToDataListValue(value);
18
+ const item = getDataListItem(dataListId, stringValue);
19
+ setDataListItem(item);
20
+ }
21
+ else {
22
+ setDataListItem(undefined);
23
+ }
24
+ }
25
+ catch (err) {
26
+ TMExceptionBoxManager.show({ exception: err });
27
+ }
28
+ }
29
+ else {
30
+ setDataListItem(undefined);
31
+ }
32
+ };
33
+ loadData();
34
+ }, [dataListId, value, loadDataListsAsync, getDataListItem, convertToDataListValue]);
35
+ if (!dataListId || value === undefined || value === null) {
36
+ return null;
37
+ }
38
+ const content = renderDataListCell(value, dataListId, viewMode);
76
39
  return showTooltip ? (_jsx(TMTooltip, { content: dataListItem ? dataListItem.value : SDKUI_Localizator.ValueNotPresent, parentStyle: { width: '100%' }, childStyle: { width: '100%' }, children: content })) : content;
77
40
  };
78
41
  export default TMDataListItemViewer;
@@ -0,0 +1,8 @@
1
+ import { DataGridTypes } from 'devextreme-react/data-grid';
2
+ interface ITMDataUserIdItemViewerProps {
3
+ userId?: number;
4
+ showIcon?: boolean;
5
+ }
6
+ declare const TMDataUserIdItemViewer: ({ userId, showIcon }: ITMDataUserIdItemViewerProps) => import("react/jsx-runtime").JSX.Element | null;
7
+ export default TMDataUserIdItemViewer;
8
+ export declare const cellRenderUserIdItem: (data: DataGridTypes.ColumnCellTemplateData, showIcon?: boolean) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { IconWarning, SDKUI_Localizator } from '../../helper';
4
+ import TMTooltip from '../base/TMTooltip';
5
+ import { TMExceptionBoxManager } from '../base/TMPopUp';
6
+ import { useDataUserIdItem } from '../../hooks/useDataUserIdItem';
7
+ import { TMColors } from '../../utils/theme';
8
+ const TMDataUserIdItemViewer = ({ userId, showIcon = false }) => {
9
+ const { loadUsersAsync, getUserItem, renderUserIdViewer } = useDataUserIdItem();
10
+ const [userItem, setUserItem] = useState(undefined);
11
+ useEffect(() => {
12
+ const loadData = async () => {
13
+ if (userId && userId > 0) {
14
+ try {
15
+ await loadUsersAsync(new Set([userId]));
16
+ const item = getUserItem(userId);
17
+ setUserItem(item);
18
+ }
19
+ catch (err) {
20
+ TMExceptionBoxManager.show({ exception: err });
21
+ }
22
+ }
23
+ else {
24
+ setUserItem(undefined);
25
+ }
26
+ };
27
+ loadData();
28
+ }, [userId, loadUsersAsync, getUserItem]);
29
+ if (!userId || userId <= 0) {
30
+ return null;
31
+ }
32
+ const content = renderUserIdViewer(userId, showIcon);
33
+ return userItem ? content :
34
+ _jsx(TMTooltip, { content: SDKUI_Localizator.ValueNotPresent, children: _jsx(IconWarning, { color: TMColors.warning }) });
35
+ };
36
+ export default TMDataUserIdItemViewer;
37
+ export const cellRenderUserIdItem = (data, showIcon) => {
38
+ return (_jsx(TMDataUserIdItemViewer, { userId: data.value, showIcon: showIcon }));
39
+ };
@@ -75,16 +75,21 @@ const TMPdfViewer = (props) => {
75
75
  const observerRef = useRef(null);
76
76
  useEffect(() => {
77
77
  const checkIsMobile = () => {
78
- const userAgent = navigator.userAgent || navigator.vendor || window.opera;
79
- // Detect actual mobile/tablet devices
80
- const isMobileDevice =
81
- // User agent detection (phones and tablets)
82
- /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) ||
83
- // Media query for touch devices with coarse pointer (more reliable than screen width)
84
- (window.matchMedia?.('(hover: none) and (pointer: coarse)').matches ?? false) ||
85
- // Touch-capable devices excluding desktop OS
86
- (navigator.maxTouchPoints > 1 && !/Win|Mac|Linux x86_64/i.test(userAgent));
87
- setIsMobile(isMobileDevice);
78
+ try {
79
+ const userAgent = navigator.userAgent || window.opera;
80
+ // Detect actual mobile/tablet devices
81
+ const isMobileDevice =
82
+ // User agent detection (phones and tablets)
83
+ /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent) ||
84
+ // Media query for touch devices with coarse pointer (more reliable than screen width)
85
+ (window.matchMedia?.('(hover: none) and (pointer: coarse)').matches ?? false) ||
86
+ // Touch-capable devices excluding desktop OS
87
+ (navigator.maxTouchPoints > 1 && !/Win|Mac|Linux x86_64/i.test(userAgent));
88
+ setIsMobile(isMobileDevice);
89
+ }
90
+ catch (error) {
91
+ setIsMobile(false);
92
+ }
88
93
  };
89
94
  const checkPdfForJavaScript = async () => {
90
95
  setIsCheckingPdf(true);
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { DataListItemDescriptor, DataListViewModes } from '@topconsultnpm/sdk-ts';
3
+ export declare const useDataListItem: () => {
4
+ loadDataListsAsync: (dataListIDs: Set<number>) => Promise<void>;
5
+ getDataListItem: (dataListID: number, value: string | number | Date) => DataListItemDescriptor | undefined;
6
+ clearCache: () => void;
7
+ hasDataList: (dataListID: number) => boolean;
8
+ renderDataListCell: (value: string | Date | number | undefined, dataListID: number, dataListViewMode: DataListViewModes) => React.ReactElement;
9
+ dataListsCache: React.MutableRefObject<Map<number, DataListItemDescriptor[]>>;
10
+ convertToDataListValue: (value: string | Date | number | undefined) => string;
11
+ convertToDataListDisplayValue: (value: string | Date | number | undefined) => string;
12
+ };
@@ -0,0 +1,131 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useCallback } from 'react';
3
+ import { DataListCacheService, DataListViewModes } from '@topconsultnpm/sdk-ts';
4
+ import { IconWarning, SDKUI_Localizator, TMImageLibrary } from '../helper';
5
+ import { FormulaHelper, StyledDivHorizontal } from '../components';
6
+ import { TMColors } from '../utils/theme';
7
+ export const useDataListItem = () => {
8
+ const dataListsCacheRef = useRef(new Map());
9
+ /**
10
+ * Converte un valore in formato stringa per DataList
11
+ * @param value Valore da convertire
12
+ * @returns Stringa rappresentante il valore
13
+ */
14
+ const convertToDataListValue = useCallback((value) => {
15
+ if (value instanceof Date) {
16
+ return value.toISOString();
17
+ }
18
+ else if (typeof value === 'number') {
19
+ return value.toString();
20
+ }
21
+ else {
22
+ return value ?? '';
23
+ }
24
+ }, []);
25
+ /**
26
+ * Converte un valore in formato stringa per visualizzazione in DataList
27
+ * @param value Valore da convertire
28
+ * @returns Stringa formattata per visualizzazione
29
+ */
30
+ const convertToDataListDisplayValue = useCallback((value) => {
31
+ if (value instanceof Date) {
32
+ return value.toLocaleDateString();
33
+ }
34
+ else if (typeof value === 'number') {
35
+ return value.toString();
36
+ }
37
+ else {
38
+ return value ?? '';
39
+ }
40
+ }, []);
41
+ /**
42
+ * Carica tutte le DataList necessarie in parallelo e popola la cache
43
+ * @param dataListIDs Set di ID delle DataList da caricare
44
+ * @returns Promise che si risolve quando tutte le DataList sono state caricate
45
+ */
46
+ const loadDataListsAsync = useCallback(async (dataListIDs) => {
47
+ if (dataListIDs.size === 0)
48
+ return;
49
+ try {
50
+ const results = await Promise.all(Array.from(dataListIDs).map(id => DataListCacheService.GetAsync(id).then(dl => ({
51
+ id,
52
+ items: dl?.items ?? []
53
+ }))));
54
+ const newCache = new Map();
55
+ results.forEach(({ id, items }) => newCache.set(id, items));
56
+ dataListsCacheRef.current = newCache;
57
+ }
58
+ catch (err) {
59
+ console.error('Error loading DataLists:', err);
60
+ dataListsCacheRef.current = new Map();
61
+ }
62
+ }, []);
63
+ /**
64
+ * Recupera un item dalla cache della DataList
65
+ * @param dataListID ID della DataList
66
+ * @param value Valore dell'item da cercare
67
+ * @returns DataListItemDescriptor se trovato, undefined altrimenti
68
+ */
69
+ const getDataListItem = useCallback((dataListID, value) => {
70
+ const stringValue = convertToDataListValue(value);
71
+ const dataListItems = dataListsCacheRef.current.get(dataListID);
72
+ return dataListItems?.find(o => o.value == stringValue);
73
+ }, []);
74
+ /**
75
+ * Svuota completamente la cache
76
+ */
77
+ const clearCache = useCallback(() => {
78
+ dataListsCacheRef.current = new Map();
79
+ }, []);
80
+ /**
81
+ * Verifica se una DataList è presente nella cache
82
+ * @param dataListID ID della DataList
83
+ * @returns true se la DataList è in cache, false altrimenti
84
+ */
85
+ const hasDataList = useCallback((dataListID) => {
86
+ return dataListsCacheRef.current.has(dataListID);
87
+ }, []);
88
+ /**
89
+ * Renderizza una cella DataList con icona e testo formattato
90
+ * @param value Valore della cella
91
+ * @param dataListID ID della DataList
92
+ * @param dataListViewMode Modalità di visualizzazione della DataList
93
+ * @returns Elemento React per la cella DataList
94
+ */
95
+ const renderDataListCell = useCallback((value, dataListID, dataListViewMode) => {
96
+ const stringValue = convertToDataListValue(value);
97
+ const showIcon = dataListViewMode !== DataListViewModes.Description;
98
+ const dataListItem = getDataListItem(dataListID, stringValue);
99
+ const getIcon = () => {
100
+ if (!showIcon)
101
+ return null;
102
+ if (value === undefined || value === null)
103
+ return null;
104
+ if (FormulaHelper.isFormula(stringValue))
105
+ return null;
106
+ return dataListItem
107
+ ? _jsx(TMImageLibrary, { imageID: dataListItem.imageID })
108
+ : _jsx(IconWarning, { color: TMColors.warning });
109
+ };
110
+ return (_jsxs(StyledDivHorizontal, { style: { width: '100%' }, title: dataListItem ? dataListItem.value : SDKUI_Localizator.ValueNotPresent, children: [getIcon(), _jsx("p", { style: {
111
+ textAlign: 'left',
112
+ marginLeft: showIcon ? '5px' : '',
113
+ opacity: dataListItem ? 1 : 0.5,
114
+ whiteSpace: 'nowrap',
115
+ overflow: 'hidden',
116
+ textOverflow: 'ellipsis',
117
+ flexGrow: 1,
118
+ minWidth: 0
119
+ }, children: dataListItem ? dataListItem.name : convertToDataListDisplayValue(value) })] }));
120
+ }, [getDataListItem]);
121
+ return {
122
+ loadDataListsAsync,
123
+ getDataListItem,
124
+ clearCache,
125
+ hasDataList,
126
+ renderDataListCell,
127
+ dataListsCache: dataListsCacheRef,
128
+ convertToDataListValue,
129
+ convertToDataListDisplayValue
130
+ };
131
+ };
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { UserDescriptor } from '@topconsultnpm/sdk-ts';
3
+ export declare const useDataUserIdItem: () => {
4
+ loadUsersAsync: (userIDs: Set<number>) => Promise<void>;
5
+ getUserItem: (userId: number) => UserDescriptor | undefined;
6
+ clearCache: () => void;
7
+ hasUser: (userId: number) => boolean;
8
+ usersCache: React.MutableRefObject<Map<number, UserDescriptor>>;
9
+ renderUserIdViewer: (userId: number | undefined, showIcon?: boolean, showTitile?: boolean) => React.ReactElement;
10
+ };
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useCallback } from 'react';
3
+ import { UserListCacheService } from '@topconsultnpm/sdk-ts';
4
+ import { IconWarning, SDKUI_Localizator } from '../helper';
5
+ import { TMColors } from '../utils/theme';
6
+ import { TMUserIcon } from '../components';
7
+ export const useDataUserIdItem = () => {
8
+ const usersCacheRef = useRef(new Map());
9
+ /**
10
+ * Carica tutti gli utenti necessari in parallelo e popola la cache
11
+ * @param userIDs Set di ID degli utenti da caricare
12
+ * @returns Promise che si risolve quando tutti gli utenti sono stati caricati
13
+ */
14
+ const loadUsersAsync = useCallback(async (userIDs) => {
15
+ if (userIDs.size === 0)
16
+ return;
17
+ try {
18
+ const results = await Promise.all(Array.from(userIDs).map(id => UserListCacheService.GetAsync(id).then(user => ({ id, user }))
19
+ .catch(() => ({ id, user: undefined }))));
20
+ const newCache = new Map();
21
+ results.forEach(({ id, user }) => {
22
+ if (user)
23
+ newCache.set(id, user);
24
+ });
25
+ usersCacheRef.current = newCache;
26
+ }
27
+ catch (err) {
28
+ console.error('Error loading Users:', err);
29
+ usersCacheRef.current = new Map();
30
+ }
31
+ }, []);
32
+ /**
33
+ * Recupera un utente dalla cache
34
+ * @param userId ID dell'utente
35
+ * @returns UserDescriptor se trovato, undefined altrimenti
36
+ */
37
+ const getUserItem = useCallback((userId) => {
38
+ return usersCacheRef.current.get(userId);
39
+ }, []);
40
+ /**
41
+ * Svuota completamente la cache
42
+ */
43
+ const clearCache = useCallback(() => {
44
+ usersCacheRef.current = new Map();
45
+ }, []);
46
+ /**
47
+ * Verifica se un utente è presente nella cache
48
+ * @param userId ID dell'utente
49
+ * @returns true se l'utente è in cache, false altrimenti
50
+ */
51
+ const hasUser = useCallback((userId) => {
52
+ return usersCacheRef.current.has(userId);
53
+ }, []);
54
+ /**
55
+ * Helper per ottenere il nome completo dell'utente
56
+ */
57
+ const getCompleteUserName = useCallback((domain, name) => {
58
+ if (!name)
59
+ return undefined;
60
+ if (!domain)
61
+ return name;
62
+ return domain + "\\" + name;
63
+ }, []);
64
+ /**
65
+ * Renderizza un componente UserIdViewer
66
+ * @param userId ID dell'utente
67
+ * @param showIcon Se mostrare l'icona
68
+ * @param noneSelectionText Testo da mostrare quando non c'è selezione
69
+ * @param TMUserIcon Componente per l'icona utente
70
+ * @returns Elemento React per visualizzare l'utente
71
+ */
72
+ const renderUserIdViewer = useCallback((userId, showIcon = false, showTitile = true) => {
73
+ const ud = userId && userId > 0 ? getUserItem(userId) : undefined;
74
+ const getIcon = () => {
75
+ if (!showIcon)
76
+ return null;
77
+ if (!userId)
78
+ return null;
79
+ return ud ? _jsx(TMUserIcon, { ud: ud }) : _jsx("div", { title: showTitile ? SDKUI_Localizator.ValueNotPresent : undefined, children: _jsx(IconWarning, { color: TMColors.warning }) });
80
+ };
81
+ const getDescription = () => {
82
+ if (!userId)
83
+ return undefined;
84
+ return ud ? getCompleteUserName(ud.domain, ud.name) : userId.toString() ?? SDKUI_Localizator.NoneSelection;
85
+ };
86
+ return (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '4px' }, children: [getIcon(), _jsx("span", { children: getDescription() })] }));
87
+ }, [getUserItem, getCompleteUserName]);
88
+ return {
89
+ loadUsersAsync,
90
+ getUserItem,
91
+ clearCache,
92
+ hasUser,
93
+ usersCache: usersCacheRef,
94
+ renderUserIdViewer
95
+ };
96
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.20.0-dev1.60",
3
+ "version": "6.20.0-dev1.62",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",