@topconsultnpm/sdkui-react 6.19.0-dev1.35 → 6.19.0-dev1.37

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,4 +1,5 @@
1
1
  import { ValidationItem } from '@topconsultnpm/sdk-ts';
2
+ export declare const sanitizeAndFormatComment: (raw?: string) => string;
2
3
  export interface ITMHtmlEditor {
3
4
  /** Width of the editor (e.g., '100%', '500px') */
4
5
  width?: string;
@@ -28,6 +29,10 @@ export interface ITMHtmlEditor {
28
29
  toolbarMode?: 'compact' | 'expanded';
29
30
  /** If true, the editor will be focused on mount */
30
31
  autoFocus?: boolean;
32
+ /** Maximum number of characters allowed in the editor */
33
+ maxLength?: number;
34
+ /** If true, displays a character count below the editor */
35
+ showCount?: boolean;
31
36
  }
32
37
  declare const TMHtmlEditor: (props: ITMHtmlEditor) => import("react/jsx-runtime").JSX.Element;
33
38
  export default TMHtmlEditor;
@@ -4,8 +4,33 @@ import HtmlEditor, { Toolbar, Item } from 'devextreme-react/html-editor';
4
4
  import ReactDOM from 'react-dom';
5
5
  import { SDKUI_Localizator } from '../../helper';
6
6
  import TMVilViewer from '../base/TMVilViewer';
7
+ import { TMMessageBoxManager, ButtonNames } from '../base/TMPopUp';
8
+ import TMTooltip from '../base/TMTooltip';
9
+ import { useDeviceType, DeviceType } from '../base/TMDeviceProvider';
10
+ export const sanitizeAndFormatComment = (raw = "") => {
11
+ if (!raw)
12
+ return "";
13
+ let cleanComment = raw
14
+ // Replace </p> with '' only if followed by <ol> or <ul>
15
+ .replace(/<\/p>(?=\s*<(ol|ul)>)/gi, '')
16
+ // Replace all other </p> with '\r\n'
17
+ .replace(/<\/p>/gi, '\r\n')
18
+ // Remove all <p> tags
19
+ .replace(/<p>/gi, '')
20
+ // Remove all <br> tags
21
+ .replace(/<br>/gi, '')
22
+ // Trim whitespace
23
+ .trim();
24
+ // Remove dangerous HTML elements
25
+ cleanComment = cleanComment.replace(/<(script|iframe|embed|object|link|style|img|video|audio|svg|form|input|button|textarea|select|pre|function)[^>]*>/gi, '');
26
+ return cleanComment;
27
+ };
7
28
  const TMHtmlEditor = (props) => {
8
- const { width = "100%", height = "100%", initialMarkup = "", mentionsConfig, onValueChanged, validationItems = [], isEditorEnabled: isEditorEnabledProp = false, toolbarMode = 'compact', autoFocus = false } = props;
29
+ const { width = "100%", height = "100%", initialMarkup = "", mentionsConfig, onValueChanged, validationItems = [], isEditorEnabled: isEditorEnabledProp = false, toolbarMode = 'compact', autoFocus = false, maxLength = 1000, showCount = true, } = props;
30
+ // Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
31
+ const deviceType = useDeviceType();
32
+ // This avoids unnecessary re-renders by only recalculating when deviceType changes.
33
+ let isMobile = useMemo(() => { return deviceType === DeviceType.MOBILE; }, [deviceType]);
9
34
  // Ref for HtmlEditor instance
10
35
  const editorRef = useRef(null);
11
36
  // State for editor enable/disable
@@ -14,13 +39,15 @@ const TMHtmlEditor = (props) => {
14
39
  const [markup, setMarkup] = useState(initialMarkup);
15
40
  // State to track the position (x, y coordinates) where the custom context menu should be displayed.
16
41
  const [contextMenuPos, setContextMenuPos] = useState(null);
17
- // State to indicate whether a paste operation is currently in progress
18
- const [isPasting, setIsPasting] = useState(false);
42
+ // State to track remaining characters
43
+ const [charactersRemaining, setCharactersRemaining] = useState(maxLength - initialMarkup.length);
44
+ useEffect(() => {
45
+ const cleanedMarkup = sanitizeAndFormatComment(markup);
46
+ setCharactersRemaining(maxLength - cleanedMarkup.length);
47
+ }, [markup, maxLength]);
19
48
  // Function to read text from the clipboard, format it, and paste into the Quill editor
20
49
  const pasteFromClipboard = async () => {
21
50
  try {
22
- // Set flag to indicate paste operation is in progress
23
- setIsPasting(true);
24
51
  // Read plain text from the clipboard asynchronously
25
52
  let text = await navigator.clipboard.readText();
26
53
  text = '<p>' + text.replace(/\r\n/g, '</p><p>') + '</p>';
@@ -52,10 +79,6 @@ const TMHtmlEditor = (props) => {
52
79
  // Log any errors that might occur during clipboard access or pasting
53
80
  console.warn('Clipboard paste failed:', err);
54
81
  }
55
- finally {
56
- // Reset paste state after a short delay to restore UI state
57
- setTimeout(() => setIsPasting(false), 500);
58
- }
59
82
  };
60
83
  useEffect(() => {
61
84
  const handleKeyDown = (e) => {
@@ -116,7 +139,7 @@ const TMHtmlEditor = (props) => {
116
139
  }, [autoFocus]);
117
140
  // Memoized check for validation errors
118
141
  const hasValidationErrors = useMemo(() => {
119
- return !isPasting && validationItems && validationItems.length > 0;
142
+ return validationItems && validationItems.length > 0;
120
143
  }, [validationItems]);
121
144
  // Handler function triggered by the context menu's "Paste" action
122
145
  const handlePaste = async () => {
@@ -125,9 +148,44 @@ const TMHtmlEditor = (props) => {
125
148
  // Close the custom context menu after pasting
126
149
  setContextMenuPos(null);
127
150
  };
128
- return (_jsxs("div", { style: { height: hasValidationErrors ? `calc(${height} - 30px)` : "100%", width: width }, onContextMenu: handleContextMenu, children: [_jsx("div", { className: "custom-mentions-wrapper", style: { borderWidth: '1px', borderStyle: 'solid', borderColor: hasValidationErrors ? "red" : "#e0e0e0 #e0e0e0 #616161", width: "100%", height: "100%" }, children: _jsx(HtmlEditor, { ref: editorRef, placeholder: SDKUI_Localizator.TypeAMessage + "...", width: "100%", height: "100%", value: markup, onValueChange: onValueChangeCallback, mentions: mentionsConfig, style: { overflow: 'hidden', outline: "none", fontSize: '1rem' }, children: isEditorEnabled && (toolbarMode === 'compact' ?
151
+ return (_jsxs("div", { style: {
152
+ height: height,
153
+ width: width
154
+ }, children: [_jsx("div", { className: "custom-mentions-wrapper", onContextMenu: handleContextMenu, style: {
155
+ borderWidth: '1px',
156
+ borderStyle: 'solid',
157
+ borderColor: hasValidationErrors ? "red" : "#e0e0e0 #e0e0e0 #616161",
158
+ width: "100%",
159
+ height: `calc(100% - ${showCount ? 15 : 0}px - ${validationItems.length > 0 ? 15 : 0}px)`,
160
+ }, children: _jsx(HtmlEditor, { ref: editorRef, placeholder: SDKUI_Localizator.TypeAMessage + "...", width: "100%", height: "100%", value: markup, onValueChange: onValueChangeCallback, mentions: mentionsConfig, style: { overflow: 'hidden', outline: "none", fontSize: '1rem' }, children: isEditorEnabled && (toolbarMode === 'compact' ?
129
161
  _jsxs(Toolbar, { multiline: false, children: [_jsx(Item, { name: "undo" }), _jsx(Item, { name: "redo" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "bold" }), _jsx(Item, { name: "italic" }), _jsx(Item, { name: "strike" }), _jsx(Item, { name: "underline" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "orderedList" }), _jsx(Item, { name: "bulletList" })] }) :
130
- _jsxs(Toolbar, { children: [_jsx(Item, { name: "undo" }), _jsx(Item, { name: "redo" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "bold" }), _jsx(Item, { name: "italic" }), _jsx(Item, { name: "strike" }), _jsx(Item, { name: "underline" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "alignLeft" }), _jsx(Item, { name: "alignCenter" }), _jsx(Item, { name: "alignRight" }), _jsx(Item, { name: "alignJustify" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "color" }), _jsx(Item, { name: "background" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "link" }), _jsx(Item, { name: "image" }), _jsx(Item, { name: "separator" })] })) }) }), !isPasting && validationItems.length > 0 && (_jsx("div", { style: { width: "100%", height: "30px" }, children: _jsx(TMVilViewer, { vil: validationItems }) })), contextMenuPos &&
162
+ _jsxs(Toolbar, { children: [_jsx(Item, { name: "undo" }), _jsx(Item, { name: "redo" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "bold" }), _jsx(Item, { name: "italic" }), _jsx(Item, { name: "strike" }), _jsx(Item, { name: "underline" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "alignLeft" }), _jsx(Item, { name: "alignCenter" }), _jsx(Item, { name: "alignRight" }), _jsx(Item, { name: "alignJustify" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "color" }), _jsx(Item, { name: "background" }), _jsx(Item, { name: "separator" }), _jsx(Item, { name: "link" }), _jsx(Item, { name: "image" }), _jsx(Item, { name: "separator" })] })) }) }), showCount ? ((() => {
163
+ const cleanedMarkup = sanitizeAndFormatComment(markup);
164
+ const showInfoIcon = charactersRemaining !== maxLength;
165
+ return (_jsxs("div", { style: {
166
+ display: 'flex',
167
+ alignItems: 'center',
168
+ justifyContent: 'flex-end',
169
+ fontSize: 12,
170
+ color: '#6c757d',
171
+ marginTop: 4,
172
+ gap: 4,
173
+ }, children: [`${Math.max(charactersRemaining, 0)} ${SDKUI_Localizator.CharactersRemaining}`, showInfoIcon && (_jsx(TMTooltip, { content: 'Markup HTML', children: _jsx("span", { className: "dx-icon-codeblock", style: { fontSize: 22, cursor: 'pointer' }, onClick: () => {
174
+ TMMessageBoxManager.show({
175
+ title: 'Markup HTML',
176
+ initialWidth: !isMobile ? '700px' : undefined,
177
+ message: (_jsx("pre", { style: {
178
+ whiteSpace: 'pre-wrap',
179
+ background: '#f5f5f5',
180
+ padding: '12px',
181
+ borderRadius: '6px',
182
+ }, children: cleanedMarkup })),
183
+ resizable: true,
184
+ showToppy: false,
185
+ buttons: [ButtonNames.OK],
186
+ });
187
+ } }) }))] }));
188
+ })()) : null, validationItems.length > 0 && _jsx(TMVilViewer, { vil: validationItems }), contextMenuPos &&
131
189
  ReactDOM.createPortal(_jsxs("div", { style: {
132
190
  position: 'fixed',
133
191
  top: contextMenuPos.y,
@@ -10,7 +10,7 @@ import { TMExceptionBoxManager, TMMessageBoxManager, ButtonNames } from '../../b
10
10
  import TMSpinner from '../../base/TMSpinner';
11
11
  import TMTooltip from '../../base/TMTooltip';
12
12
  import TMUserAvatar from '../../base/TMUserAvatar';
13
- import TMHtmlEditor from '../../editors/TMHtmlEditor';
13
+ import TMHtmlEditor, { sanitizeAndFormatComment } from '../../editors/TMHtmlEditor';
14
14
  import TMChooserForm from '../../forms/TMChooserForm';
15
15
  import TMSaveForm from '../../forms/TMSaveForm';
16
16
  const getNonDirectoryFiles = (items, exclude) => {
@@ -28,6 +28,7 @@ const getNonDirectoryFiles = (items, exclude) => {
28
28
  });
29
29
  };
30
30
  const TMBlogCommentForm = (props) => {
31
+ const maxLength = 1000;
31
32
  const { participants, selectedAttachments, selectedAttachmentDid, allFileItems, allArchivedDocumentsFileItems = [], onClose, refreshCallback, context, showAttachmentsSection = true, updateShouldSelectLastBlog } = props;
32
33
  // Initialize state with combined array
33
34
  const [dataSource, setDataSource] = useState(() => [...getNonDirectoryFiles(allFileItems?.items || [], []), ...allArchivedDocumentsFileItems]);
@@ -39,20 +40,18 @@ const TMBlogCommentForm = (props) => {
39
40
  const validator = async (params) => {
40
41
  let vil = [];
41
42
  const { comment } = params;
43
+ const cleanedMarkup = sanitizeAndFormatComment(comment);
42
44
  // Check for empty comment
43
- if (!comment || comment.trim() === "") {
45
+ if (!cleanedMarkup || cleanedMarkup.trim() === "") {
44
46
  vil.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.Comment, `${SDK_Localizator.RequiredField}`));
45
47
  }
46
48
  else {
47
- // Imposta la lunghezza massima consentita per il commento
48
- const maxLength = 1000;
49
49
  // Calcola la lunghezza effettiva del commento inserito
50
- const commentLength = comment.length;
51
- // Calcola di quanto la lunghezza del commento supera il limite massimo
52
- const exceededLength = commentLength - maxLength;
50
+ const commentLength = cleanedMarkup.length;
51
+ const exceededChars = commentLength - maxLength;
53
52
  // Validate description length (max 1000)
54
53
  if (commentLength > maxLength) {
55
- vil.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.Comment, `${SDKUI_Localizator.DescriptionLengthExceededMessage.replaceParams(commentLength, maxLength, exceededLength)}`));
54
+ vil.push(new ValidationItem(ResultTypes.ERROR, SDKUI_Localizator.Comment, `${SDKUI_Localizator.DescriptionLengthExceededMessage.replaceParams(exceededChars)}`));
56
55
  }
57
56
  // Detect dangerous HTML tags
58
57
  const tagRegex = /<\/?(script|iframe|embed|object|link|style|img|video|audio|svg|form|input|button|textarea|select|pre|function)[^>]*>/gi;
@@ -115,19 +114,7 @@ const TMBlogCommentForm = (props) => {
115
114
  // Retrieve the comment from formData, or use an empty string if undefined
116
115
  const comment = formData?.comment ?? "";
117
116
  // Clean the comment by removing <p> tags and replacing </p> with line breaks
118
- let cleanComment = comment
119
- // Replace </p> with '' only if followed by <ol> or <ul>
120
- .replace(/<\/p>(?=\s*<(ol|ul)>)/gi, '')
121
- // Replace all other </p> with '\r\n'
122
- .replace(/<\/p>/gi, '\r\n')
123
- // Remove all <p> tags
124
- .replace(/<p>/gi, '')
125
- // Remove all <br> tags
126
- .replace(/<br>/gi, '')
127
- // Trim whitespace
128
- .trim();
129
- // Validate and remove any potentially dangerous HTML tags (like <script>, <iframe>, etc.)
130
- cleanComment = cleanComment.replace(/<(script|iframe|embed|object|link|style|img|video|audio|svg|form|input|button|textarea|select|pre|function)[^>]*>/gi, '');
117
+ const cleanComment = sanitizeAndFormatComment(comment);
131
118
  // Assign the cleaned comment as the description for the blog post
132
119
  blogPost.description = cleanComment ?? "";
133
120
  // If there are file items (attachments), process them
@@ -220,7 +207,7 @@ const TMBlogCommentForm = (props) => {
220
207
  // Update the state with selected draft items
221
208
  setCurrentDraftAttachments(selectedDraftItems);
222
209
  };
223
- return _jsx(TMSaveForm, { id: 1, title: SDKUI_Localizator.AddNewComment, showTitleFormMode: false, showErrorCount: false, customSaveButton: _jsx("i", { className: 'dx-icon-send' }), customTooltipSaveButton: SDKUI_Localizator.Send, showUndoButton: false, hasNavigation: false, skipIsModifiedCheck: true, isModal: true, width: calcResponsiveSizes(deviceType, '800px', '800px', '95%'), height: '550px', formMode: FormModes.Create, validationItems: validationItems, exception: exception, isModified: calcIsModified(formData, formDataOrig), onSaveAsync: onSaveAsync, onClose: onCloseCallback, customToolbarElements: _jsx("div", { style: { display: 'flex', gap: '2px' }, children: _jsx(TMButton, { btnStyle: "toolbar", icon: isEditorEnabled ? _jsx("i", { className: 'dx-icon-font' }) : _jsx("i", { className: 'dx-icon-background' }), caption: isEditorEnabled ? SDKUI_Localizator.HideFormattingOptions : SDKUI_Localizator.ShowFormattingOptions, onClick: toggleEditorMode }) }), children: _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { style: { width: "100%", height: showAttachmentsSection ? "calc(100% - 60px)" : "100%" }, children: _jsx(TMHtmlEditor, { width: '100%', height: '100%', isEditorEnabled: isEditorEnabled, validationItems: validationItems, onValueChanged: onValueChanged, mentionsConfig: mentionsConfig, autoFocus: true }) }), showAttachmentsSection && _jsxs("div", { style: { display: 'flex', alignItems: 'center', height: '60px', marginTop: '10px' }, children: [_jsx("div", { style: {
210
+ return _jsx(TMSaveForm, { id: 1, title: SDKUI_Localizator.AddNewComment, showTitleFormMode: false, showErrorCount: false, customSaveButton: _jsx("i", { className: 'dx-icon-send' }), customTooltipSaveButton: SDKUI_Localizator.Send, showUndoButton: false, hasNavigation: false, skipIsModifiedCheck: true, isModal: true, width: calcResponsiveSizes(deviceType, '800px', '800px', '95%'), height: '550px', formMode: FormModes.Create, validationItems: validationItems, exception: exception, isModified: calcIsModified(formData, formDataOrig), onSaveAsync: onSaveAsync, onClose: onCloseCallback, customToolbarElements: _jsx("div", { style: { display: 'flex', gap: '2px' }, children: _jsx(TMButton, { btnStyle: "toolbar", icon: isEditorEnabled ? _jsx("i", { className: 'dx-icon-font' }) : _jsx("i", { className: 'dx-icon-background' }), caption: isEditorEnabled ? SDKUI_Localizator.HideFormattingOptions : SDKUI_Localizator.ShowFormattingOptions, onClick: toggleEditorMode }) }), children: _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { style: { width: "100%", height: showAttachmentsSection ? "calc(100% - 60px)" : "100%" }, children: _jsx(TMHtmlEditor, { width: '100%', height: '100%', isEditorEnabled: isEditorEnabled, validationItems: validationItems, onValueChanged: onValueChanged, mentionsConfig: mentionsConfig, autoFocus: true, maxLength: maxLength }) }), showAttachmentsSection && _jsxs("div", { style: { display: 'flex', alignItems: 'center', height: '60px', marginTop: '10px' }, children: [_jsx("div", { style: {
224
211
  width: 'calc(100% - 60px)',
225
212
  overflowX: 'auto',
226
213
  whiteSpace: 'nowrap',
@@ -235,14 +222,22 @@ const TMBlogCommentForm = (props) => {
235
222
  const tooltipContent = (_jsxs("div", { style: { textAlign: 'left' }, children: [_jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Name, ":"] }), " ", draft.name ?? '-'] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Author, ":"] }), " ", draft.updaterName ?? '-'] }), _jsx("hr", {}), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Version, ":"] }), " ", draft.version] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.Size, ":"] }), " ", formatBytes(draft.size ?? 0)] }), _jsx("hr", {}), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.CreationTime, ":"] }), " ", Globalization.getDateTimeDisplayValue(draft.creationTime)] }), _jsxs("div", { children: [_jsxs("span", { style: { fontWeight: 'bold' }, children: [SDKUI_Localizator.LastUpdateTime, ":"] }), " ", Globalization.getDateTimeDisplayValue(draft.lastUpdateTime)] })] }));
236
223
  return _jsxs("div", { style: {
237
224
  display: 'inline-flex',
238
- alignItems: 'center',
239
- padding: '8px 12px',
240
- marginRight: '8px',
225
+ alignItems: 'center', // <-- this centers content vertically
226
+ padding: '4px 8px',
227
+ margin: '4px',
241
228
  border: '1px solid #ddd',
242
229
  borderRadius: '8px',
230
+ boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
231
+ cursor: 'pointer',
232
+ fontSize: '0.9rem',
243
233
  backgroundColor: '#fff',
244
- boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
245
- }, children: [draft.ext ? (_jsx("span", { style: { marginRight: '10px' }, children: getFileIcon(draft.ext, undefined, tooltipContent) })) : (_jsx(IconAttachment, { style: { marginRight: '5px' } })), _jsx("span", { style: { marginRight: '8px' }, children: draft.name }), draft.version && _jsx("span", { style: {
234
+ }, onMouseEnter: (e) => {
235
+ e.currentTarget.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.15)';
236
+ e.currentTarget.style.backgroundColor = '#cfcfcf';
237
+ }, onMouseLeave: (e) => {
238
+ e.currentTarget.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)';
239
+ e.currentTarget.style.backgroundColor = '#fff';
240
+ }, children: [draft.ext ? (_jsx("span", { style: { marginRight: '10px', display: 'flex', alignItems: 'center' }, children: getFileIcon(draft.ext, undefined, tooltipContent) })) : (_jsx(IconAttachment, { style: { marginRight: '5px' } })), _jsx("span", { style: { marginRight: '8px', display: 'flex', alignItems: 'center' }, children: draft.name }), draft.version && (_jsx("span", { style: {
246
241
  display: 'inline-flex',
247
242
  width: '20px',
248
243
  height: '20px',
@@ -254,7 +249,7 @@ const TMBlogCommentForm = (props) => {
254
249
  fontWeight: 'bold',
255
250
  marginRight: '8px',
256
251
  boxShadow: '1px 1px 2px #00000020',
257
- }, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Version, children: draft.version }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.RemoveAttachment, children: _jsx("span", { onClick: () => removeAttachment(draft), style: {
252
+ }, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Version, children: draft.version }) })), _jsx(TMTooltip, { content: SDKUI_Localizator.RemoveAttachment, children: _jsx("span", { onClick: () => removeAttachment(draft), style: {
258
253
  display: 'inline-flex',
259
254
  width: '20px',
260
255
  height: '20px',
@@ -265,21 +260,36 @@ const TMBlogCommentForm = (props) => {
265
260
  borderRadius: '30px',
266
261
  cursor: 'pointer',
267
262
  boxShadow: '1px 1px 2px #00000020',
268
- }, children: _jsx("span", { style: { fontSize: "15px" }, children: "\u00D7" }) }) })] }, draft.did);
269
- })) : (_jsx("div", { style: { color: '#999', width: '100%', textAlign: 'center' }, children: SDKUI_Localizator.NoAttachments })) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Attachments, children: _jsx("i", { className: "dx-icon-attach", style: {
270
- width: '50px',
271
- height: '50px',
272
- marginLeft: '10px',
273
- fontSize: '20px',
274
- cursor: 'pointer',
275
- border: '1px solid #ccc',
276
- borderRadius: '8px',
277
- backgroundColor: '#f9f9f9',
278
- display: 'flex',
279
- alignItems: 'center',
280
- justifyContent: 'center',
281
- transition: 'all 0.2s ease-in-out',
282
- }, onMouseOver: (e) => (e.currentTarget.style.backgroundColor = '#e6f7ff'), onMouseOut: (e) => (e.currentTarget.style.backgroundColor = '#f9f9f9'), onClick: () => setShowAttachmentsForm(true) }) })] })] }), showAttachmentsForm && _jsx(TMAttachmentsView, { dataSource: dataSource, selectedIDs: currentDraftAttachments.map(draft => draft.id), onChoose: onChoose, onClose: () => setShowAttachmentsForm(false) })] }) });
263
+ }, children: _jsx("span", { style: { fontSize: '15px' }, children: "\u00D7" }) }) })] }, draft.did);
264
+ })) : (_jsx("div", { style: { color: '#999', width: '100%', textAlign: 'center' }, children: SDKUI_Localizator.NoAttachments })) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Attachments + ": " + currentDraftAttachments.length, children: _jsxs("div", { style: { position: 'relative', display: 'inline-block' }, children: [_jsx("i", { className: "dx-icon-attach", style: {
265
+ width: '50px',
266
+ height: '50px',
267
+ marginLeft: '10px',
268
+ fontSize: '20px',
269
+ cursor: 'pointer',
270
+ border: '1px solid #ccc',
271
+ borderRadius: '8px',
272
+ backgroundColor: '#f9f9f9',
273
+ display: 'flex',
274
+ alignItems: 'center',
275
+ justifyContent: 'center',
276
+ transition: 'all 0.2s ease-in-out',
277
+ }, onMouseOver: (e) => (e.currentTarget.style.backgroundColor = '#e6f7ff'), onMouseOut: (e) => (e.currentTarget.style.backgroundColor = '#f9f9f9'), onClick: () => setShowAttachmentsForm(true) }), currentDraftAttachments.length > 0 && (_jsx("span", { style: {
278
+ position: 'absolute',
279
+ top: '-5px',
280
+ right: '-5px',
281
+ backgroundColor: '#203E5A',
282
+ color: 'white',
283
+ borderRadius: '50%',
284
+ minWidth: '20px',
285
+ height: '20px',
286
+ display: 'flex',
287
+ alignItems: 'center',
288
+ justifyContent: 'center',
289
+ padding: '0 6px',
290
+ fontSize: '12px',
291
+ fontWeight: 'bold',
292
+ }, children: currentDraftAttachments.length > 99 ? '99+' : currentDraftAttachments.length }))] }) })] })] }), showAttachmentsForm && _jsx(TMAttachmentsView, { dataSource: dataSource, selectedIDs: currentDraftAttachments.map(draft => draft.id), onChoose: onChoose, onClose: () => setShowAttachmentsForm(false) })] }) });
283
293
  };
284
294
  export default TMBlogCommentForm;
285
295
  const TMAttachmentsView = (props) => {
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
3
- import { SDK_Globals, DataColumnTypes, MetadataDataDomains, DataListViewModes, MetadataFormats, LayoutModes, TemplateTIDs, DcmtTypeListCacheService, AccessLevels, SystemMIDsAsNumber } from '@topconsultnpm/sdk-ts';
3
+ import { SDK_Globals, DataColumnTypes, MetadataDataDomains, DataListViewModes, MetadataFormats, LayoutModes, TemplateTIDs, DcmtTypeListCacheService, AccessLevels, SystemMIDsAsNumber, RetrieveFileOptions, DcmtOpers, GeneralRetrieveFormats } from '@topconsultnpm/sdk-ts';
4
4
  import styled from 'styled-components';
5
5
  import { getCommandsMenuItems, getSelectedDcmtsOrFocused } from './TMSearchResultsMenuItems';
6
6
  import { genUniqueId, IconShow, IconBoard, IconDcmtTypeSys, IconDetailDcmts, SDKUI_Localizator, IconDelete, IconRefresh, IconMenuVertical, IconDownload, deepCompare, getDataColumnName, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, IconSearchCheck, TMCommandsContextMenu } from '../../../helper';
@@ -83,11 +83,12 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
83
83
  const [currentMetadataValues, setCurrentMetadataValues] = useState([]);
84
84
  const [dcmtFormLayoutMode, setDcmtFormLayoutMode] = useState(LayoutModes.Update);
85
85
  const [isModifiedBatchUpdate, setIsModifiedBatchUpdate] = useState(false);
86
+ const [sharedDcmtFile, setSharedDcmtFile] = useState(undefined);
86
87
  // State to control whether the export form (for exporting to Excel/CSV/txt and others) should be shown
87
88
  const [showExportForm, setShowExportForm] = useState(false);
88
89
  const [confirmFormat, ConfirmFormatDialog] = useInputCvtFormatDialog();
89
90
  const { openConfirmAttachmentsDialog, ConfirmAttachmentsDialog } = useInputAttachmentsDialog();
90
- const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync } = useDcmtOperations();
91
+ const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync, getDcmtFileAsync } = useDcmtOperations();
91
92
  const relatedDocuments = useRelatedDocuments({ selectedSearchResult, focusedItem, currentSearchResults });
92
93
  const { relatedDcmts, showRelatedDcmtsChooser, archiveType, isOpenDetails, isOpenMaster, isOpenArchiveRelationForm, archiveRelatedDcmtFormTID, archiveRelatedDcmtFormMids, relatedDcmtsChooserDataSource, showPairDcmtsModal, isPairingManyToMany, pairedSearchResults, manyToManyRelations, selectedManyToManyRelation, showManyToManyChooser, manyToManyChooserDataSource, showPairSearchModal, pairSearchModalTargetTID, pairSearchModalParentTID, pairSearchModalParentDID, pairSearchModalRelation, pairSearchModalInputMids, currentTIDHasDetailRelations, currentTIDHasMasterRelations, canArchiveMasterRelation, canArchiveDetailRelation, hasManyToManyRelation, setIsOpenDetails, setIsOpenMaster, setShowRelatedDcmtsChooser, setShowManyToManyChooser, setShowPairDcmtsModal, setShowPairSearchModal, setIsOpenArchiveRelationForm, setArchiveType, setArchiveRelatedDcmtFormTID, setArchiveRelatedDcmtFormMids, pairFloatingActionConfig, pairSearchModalFloatingActionConfig, archiveMasterDocuments, archiveDetailDocuments, pairManyToMany, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, archiveRelatedDcmtHandler, executeManyToManyPairing, } = relatedDocuments;
93
94
  const deviceType = useDeviceType();
@@ -149,12 +150,24 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
149
150
  });
150
151
  }, [currentMetadataValues]);
151
152
  const openFormHandler = (layoutMode) => { setIsOpenDcmtForm(true); setDcmtFormLayoutMode(layoutMode); };
152
- const openSharedArchiveHandler = () => {
153
+ const openSharedArchiveHandler = async () => {
153
154
  const dcmts = getSelectedDcmtsOrFocused(selectedItems, focusedItem);
154
155
  if (dcmts.length === 0) {
155
- ShowAlert({ message: "Nessun documento selezionato", mode: "warning", duration: 3000 });
156
+ ShowAlert({ message: "Nessun documento selezionato", mode: "warning", title: 'Archivio Condivisa', duration: 3000 });
156
157
  return;
157
158
  }
159
+ try {
160
+ const rfo = new RetrieveFileOptions();
161
+ rfo.retrieveReason = DcmtOpers.None;
162
+ rfo.generalRetrieveFormat = GeneralRetrieveFormats.OriginalUnsigned;
163
+ let dcmtFile = await getDcmtFileAsync({ TID: focusedItem?.TID, DID: focusedItem?.DID }, rfo, 'Archiviazione Condivisa', false, true);
164
+ if (dcmtFile) {
165
+ setSharedDcmtFile(dcmtFile?.file);
166
+ }
167
+ }
168
+ catch (error) {
169
+ ShowAlert({ message: 'Il Documenti ha solo i metadati', mode: "warning", title: 'Archivio Condivisa', duration: 5000 });
170
+ }
158
171
  setIsOpenSharedArchive(true);
159
172
  };
160
173
  const openTaskFormHandler = (onTaskCreated) => {
@@ -608,7 +621,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
608
621
  await onRefreshSearchAsync?.();
609
622
  } }), isOpenSharedArchive && _jsx(TMModal, { title: "Archiviazione condivisa", onClose: () => {
610
623
  setIsOpenSharedArchive(false);
611
- }, width: isMobile ? '90%' : '80%', height: isMobile ? '90%' : '80%', children: _jsx(TMArchive, { inputTID: focusedItem?.TID, inputMids: currentMetadataValues.filter(md => md.mid && md.mid > 100).map(md => ({ mid: md.mid, value: md.value ?? '' })), isSharedArchive: true, onSavedAsyncCallback: async (tid, did) => {
624
+ }, width: isMobile ? '90%' : '80%', height: isMobile ? '90%' : '80%', children: _jsx(TMArchive, { inputTID: focusedItem?.TID, inputMids: currentMetadataValues.filter(md => md.mid && md.mid > 100).map(md => ({ mid: md.mid, value: md.value ?? '' })), isSharedArchive: true, inputFile: sharedDcmtFile, onSavedAsyncCallback: async (tid, did) => {
612
625
  setIsOpenSharedArchive(false);
613
626
  await onRefreshSearchAsync?.();
614
627
  } }) })] }));
@@ -4,7 +4,7 @@ import { TMDataGridContextMenuItem } from '../../base/TMDataGrid';
4
4
  import { DcmtInfo, DcmtOperationTypes, DownloadModes, DownloadTypes, SearchResultContext } from '../../../ts';
5
5
  export declare const getSelectedDcmtsOrFocused: (selectedItems: Array<any>, focusedItem: any, fileFormat?: FileFormats) => DcmtInfo[];
6
6
  export declare const signatureInformationCallback: (isMobile: boolean, inputDcmts: DcmtInfo[] | undefined) => Promise<void>;
7
- export declare const getCommandsMenuItems: (isMobile: boolean, dtd: DcmtTypeDescriptor | undefined, selectedItems: Array<any>, focusedItem: any, context: SearchResultContext, showFloatingBar: boolean, workingGroupContext: WorkingGroupDescriptor | undefined, showSearch: boolean, setShowFloatingBar: React.Dispatch<React.SetStateAction<boolean>>, openFormHandler: (layoutMode: LayoutModes) => void, openSharedArchiveHandler: () => void, downloadDcmtsAsync: (inputDcmts: DcmtInfo[] | undefined, downloadType: DownloadTypes, downloadMode: DownloadModes, onFileDownloaded?: (dcmtFile: File | undefined) => void, confirmAttachments?: (list: FileDescriptor[]) => Promise<string[] | undefined>) => Promise<void>, runOperationAsync: (inputDcmts: DcmtInfo[] | undefined, dcmtOperationType: DcmtOperationTypes, actionAfterOperationAsync?: () => Promise<void>) => Promise<void>, onRefreshSearchAsync: (() => Promise<void>) | undefined, onRefreshDataRowsAsync: (() => Promise<void>) | undefined, onRefreshAfterAddDcmtToFavs: (() => void) | undefined, confirmFormat: () => Promise<FileFormats>, confirmAttachments: (list: FileDescriptor[]) => Promise<string[] | undefined>, openTaskFormHandler: () => void, openDetailDcmtsFormHandler: (value: boolean) => void, openMasterDcmtsFormHandler: (value: boolean) => void, openBatchUpdateFormHandler: (value: boolean) => void, openExportForm: () => void, handleToggleSearch: () => void, handleSignApprove: () => void, openWGsCopyMoveForm?: ((mode: "copyToWgDraft" | "copyToWgArchivedDoc", dcmtTypeDescriptor: DcmtTypeDescriptor, documents: Array<DcmtInfo>) => void), openCommentFormCallback?: ((documents: Array<DcmtInfo>) => void), openEditPdf?: ((documents: Array<DcmtInfo>) => void), openAddDocumentForm?: () => void, passToArchiveCallback?: (outputMids: Array<{
7
+ export declare const getCommandsMenuItems: (isMobile: boolean, dtd: DcmtTypeDescriptor | undefined, selectedItems: Array<any>, focusedItem: any, context: SearchResultContext, showFloatingBar: boolean, workingGroupContext: WorkingGroupDescriptor | undefined, showSearch: boolean, setShowFloatingBar: React.Dispatch<React.SetStateAction<boolean>>, openFormHandler: (layoutMode: LayoutModes) => void, openSharedArchiveHandler: () => Promise<void>, downloadDcmtsAsync: (inputDcmts: DcmtInfo[] | undefined, downloadType: DownloadTypes, downloadMode: DownloadModes, onFileDownloaded?: (dcmtFile: File | undefined) => void, confirmAttachments?: (list: FileDescriptor[]) => Promise<string[] | undefined>) => Promise<void>, runOperationAsync: (inputDcmts: DcmtInfo[] | undefined, dcmtOperationType: DcmtOperationTypes, actionAfterOperationAsync?: () => Promise<void>) => Promise<void>, onRefreshSearchAsync: (() => Promise<void>) | undefined, onRefreshDataRowsAsync: (() => Promise<void>) | undefined, onRefreshAfterAddDcmtToFavs: (() => void) | undefined, confirmFormat: () => Promise<FileFormats>, confirmAttachments: (list: FileDescriptor[]) => Promise<string[] | undefined>, openTaskFormHandler: () => void, openDetailDcmtsFormHandler: (value: boolean) => void, openMasterDcmtsFormHandler: (value: boolean) => void, openBatchUpdateFormHandler: (value: boolean) => void, openExportForm: () => void, handleToggleSearch: () => void, handleSignApprove: () => void, openWGsCopyMoveForm?: ((mode: "copyToWgDraft" | "copyToWgArchivedDoc", dcmtTypeDescriptor: DcmtTypeDescriptor, documents: Array<DcmtInfo>) => void), openCommentFormCallback?: ((documents: Array<DcmtInfo>) => void), openEditPdf?: ((documents: Array<DcmtInfo>) => void), openAddDocumentForm?: () => void, passToArchiveCallback?: (outputMids: Array<{
8
8
  mid: number;
9
9
  value: string;
10
10
  }>) => void, archiveMasterDocuments?: (tid: number | undefined) => Promise<void>, archiveDetailDocuments?: (tid: number | undefined) => Promise<void>, hasMasterRelation?: boolean, hasDetailRelation?: boolean, canArchiveMasterRelation?: boolean, canArchiveDetailRelation?: boolean, pairManyToManyDocuments?: (isPairing: boolean) => Promise<void>, hasManyToManyRelation?: boolean) => Array<TMDataGridContextMenuItem>;
@@ -406,13 +406,13 @@ export const getCommandsMenuItems = (isMobile, dtd, selectedItems, focusedItem,
406
406
  text: "Archiviazione condivisa",
407
407
  operationType: 'singleRow',
408
408
  disabled: disabledForSingleRow(selectedItems, focusedItem),
409
- onClick: () => { openSharedArchiveHandler(); }
409
+ onClick: async () => { await openSharedArchiveHandler(); }
410
410
  },
411
411
  {
412
412
  icon: svgToString(_jsx(IconSharedDcmt, {})),
413
413
  text: "Mostra documenti condivisi",
414
414
  operationType: 'multiRow',
415
- disabled: !hasManyToManyRelation || disabledForMultiRow(selectedItems, focusedItem),
415
+ disabled: disabledForMultiRow(selectedItems, focusedItem),
416
416
  onClick: () => ShowAlert({ message: "TODO Mostra documenti condivisi", mode: 'info', title: `${"TODO"}`, duration: 3000 })
417
417
  }
418
418
  ]
@@ -132,7 +132,7 @@ export declare class SDKUI_Localizator {
132
132
  static get DeletionOperationInterrupted(): "Löschvorgang abgebrochen" | "Deletion operation interrupted" | "Operación de eliminación interrumpida" | "Opération de suppression interrompue" | "Operação de exclusão interrompida" | "Operazione di eliminazione interrotta";
133
133
  static get Deny(): "Ablehnen" | "Deny" | "Denegar" | "Refuser" | "Negar" | "Negato";
134
134
  static get Description(): "Beschreibung" | "Description" | "Descripción" | "Descrição" | "Descrizione";
135
- static get DescriptionLengthExceededMessage(): "Die Beschreibung ist zu lang: {{0}}/{{1}} Zeichen ({{2}} zu viel)" | "Description is too long: {{0}}/{{1}} characters ({{2}} too many)" | "La descripción es demasiado larga: {{0}}/{{1}} caracteres ({{2}} de más)" | "La description est trop longue : {{0}}/{{1}} caractères ({{2}} en trop)" | "A descrição é demasiado longa: {{0}}/{{1}} caracteres ({{2}} a mais)" | "La descrizione è troppo lunga: {{0}}/{{1}} caratteri ({{2}} in più)";
135
+ static get DescriptionLengthExceededMessage(): "Die Beschreibung ist zu lang: {{0}} Zeichen zu viel" | "Description is too long: {{0}} characters too many" | "La descripción es demasiado larga: {{0}} caracteres de más" | "La description est trop longue : {{0}} caractères en trop" | "A descrição é demasiado longa: {{0}} caracteres a mais" | "La descrizione è troppo lunga: {{0}} caratteri in più";
136
136
  static get DescriptionTooLongMessage(): "Die Beschreibung ist zu lang: Maximal {{0}' Zeichen" | "Description is too long: Max {{0}} characters" | "La descripción es demasiado larga: Máximo {{0}} caracteres" | "La description est trop longue : Maximum {{0}} caractères" | "A descrição é demasiado longa: Máximo {{0}} caracteres" | "La descrizione è troppo lunga: Massimo {{0}} caratteri";
137
137
  static get Design(): "Design" | "Diseño" | "Conception" | "Projeto" | "Progettazione";
138
138
  static get Destination(): "Bestimmung" | "Destination" | "Destino" | "Destinazione";
@@ -1278,17 +1278,17 @@ export class SDKUI_Localizator {
1278
1278
  static get DescriptionLengthExceededMessage() {
1279
1279
  switch (this._cultureID) {
1280
1280
  case CultureIDs.De_DE:
1281
- return "Die Beschreibung ist zu lang: {{0}}/{{1}} Zeichen ({{2}} zu viel)";
1281
+ return "Die Beschreibung ist zu lang: {{0}} Zeichen zu viel";
1282
1282
  case CultureIDs.En_US:
1283
- return "Description is too long: {{0}}/{{1}} characters ({{2}} too many)";
1283
+ return "Description is too long: {{0}} characters too many";
1284
1284
  case CultureIDs.Es_ES:
1285
- return "La descripción es demasiado larga: {{0}}/{{1}} caracteres ({{2}} de más)";
1285
+ return "La descripción es demasiado larga: {{0}} caracteres de más";
1286
1286
  case CultureIDs.Fr_FR:
1287
- return "La description est trop longue : {{0}}/{{1}} caractères ({{2}} en trop)";
1287
+ return "La description est trop longue : {{0}} caractères en trop";
1288
1288
  case CultureIDs.Pt_PT:
1289
- return "A descrição é demasiado longa: {{0}}/{{1}} caracteres ({{2}} a mais)";
1289
+ return "A descrição é demasiado longa: {{0}} caracteres a mais";
1290
1290
  default:
1291
- return "La descrizione è troppo lunga: {{0}}/{{1}} caratteri ({{2}} in più)";
1291
+ return "La descrizione è troppo lunga: {{0}} caratteri in più";
1292
1292
  }
1293
1293
  }
1294
1294
  static get DescriptionTooLongMessage() {
@@ -346,9 +346,11 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
346
346
  const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
347
347
  if (!focusedItem?.TID || !focusedItem?.DID) {
348
348
  ShowAlert({
349
- message: "Nessun documento selezionato per l'operazione molti-a-molti.",
349
+ message: isPairing
350
+ ? "Nessun documento selezionato per l'abbinamento molti a molti."
351
+ : "Nessun documento selezionato per il disabbinamento molti a molti.",
350
352
  mode: 'warning',
351
- title: 'Operazione molti-a-molti',
353
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
352
354
  duration: 5000
353
355
  });
354
356
  return;
@@ -363,9 +365,11 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
363
365
  }
364
366
  if (!qd) {
365
367
  ShowAlert({
366
- message: "Nessuna query di recupero associata alla relazione molti-a-molti.",
368
+ message: isPairing
369
+ ? "Nessuna query di recupero associata alla relazione di abbinamento molti a molti."
370
+ : "Nessuna query di recupero associata alla relazione di disabbinamento molti a molti.",
367
371
  mode: 'warning',
368
- title: 'Operazione molti-a-molti',
372
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
369
373
  duration: 5000
370
374
  });
371
375
  return;
@@ -470,7 +474,7 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
470
474
  ShowAlert({
471
475
  message: "Nessun documento abbinato trovato.",
472
476
  mode: 'warning',
473
- title: 'Operazione molti-a-molti',
477
+ title: 'Disabbina documenti molti a molti',
474
478
  duration: 5000
475
479
  });
476
480
  return;
@@ -494,7 +498,7 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
494
498
  ShowAlert({
495
499
  message: "Nessun documento trovato.",
496
500
  mode: 'warning',
497
- title: 'Operazione molti-a-molti',
501
+ title: 'Abbina documenti molti a molti',
498
502
  duration: 5000
499
503
  });
500
504
  openPairSearchModal(relation, targetTID, qd);
@@ -519,7 +523,7 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
519
523
  ShowAlert({
520
524
  message: "Nessun documento da abbinare. Tutti i documenti risultanti sono già abbinati.",
521
525
  mode: 'warning',
522
- title: 'Operazione molti-a-molti',
526
+ title: 'Abbina documenti molti a molti',
523
527
  duration: 5000
524
528
  });
525
529
  openPairSearchModal(relation, targetTID, qd);
@@ -537,9 +541,11 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
537
541
  return;
538
542
  if (!relations.some(r => r.relationType === RelationTypes.ManyToMany)) {
539
543
  ShowAlert({
540
- message: "Nessuna relazione molti-a-molti definita nel sistema.",
544
+ message: isPairing
545
+ ? "Nessuna relazione di abbinamento molti a molti definita nel sistema."
546
+ : "Nessuna relazione di disabbinamento molti a molti definita nel sistema.",
541
547
  mode: 'warning',
542
- title: 'Operazione molti-a-molti',
548
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
543
549
  duration: 5000
544
550
  });
545
551
  return;
@@ -547,9 +553,11 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
547
553
  const manyToManyRels = relations.filter(r => r.relationType === RelationTypes.ManyToMany);
548
554
  if (!manyToManyRels.some(r => r.masterTID === selectedSearchResult?.fromTID || r.detailTID === selectedSearchResult?.fromTID)) {
549
555
  ShowAlert({
550
- message: "Nessuna relazione molti-a-molti definita per il tipo di documento selezionato.",
556
+ message: isPairing
557
+ ? "Nessuna relazione di abbinamento molti a molti definita per il tipo di documento selezionato."
558
+ : "Nessuna relazione di disabbinamento molti a molti definita per il tipo di documento selezionato.",
551
559
  mode: 'warning',
552
- title: 'Operazione molti-a-molti',
560
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
553
561
  duration: 5000
554
562
  });
555
563
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.19.0-dev1.35",
3
+ "version": "6.19.0-dev1.37",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",