@topconsultnpm/sdkui-react 6.19.0-dev1.9 → 6.19.0-dev2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/lib/components/base/Styled.d.ts +1 -0
  2. package/lib/components/base/Styled.js +40 -0
  3. package/lib/components/base/TMCustomButton.d.ts +11 -0
  4. package/lib/components/base/TMCustomButton.js +63 -0
  5. package/lib/components/base/TMFileManagerDataGridView.js +4 -1
  6. package/lib/components/base/TMLayout.d.ts +2 -1
  7. package/lib/components/base/TMLayout.js +2 -2
  8. package/lib/components/base/TMPopUp.js +5 -18
  9. package/lib/components/base/TMTreeView.js +3 -2
  10. package/lib/components/editors/TMHtmlEditor.d.ts +5 -0
  11. package/lib/components/editors/TMHtmlEditor.js +72 -12
  12. package/lib/components/editors/TMMetadataValues.js +90 -40
  13. package/lib/components/features/archive/TMArchive.d.ts +10 -0
  14. package/lib/components/features/archive/TMArchive.js +56 -25
  15. package/lib/components/features/blog/TMBlogCommentForm.d.ts +4 -4
  16. package/lib/components/features/blog/TMBlogCommentForm.js +76 -51
  17. package/lib/components/features/documents/TMDcmtBlog.d.ts +15 -0
  18. package/lib/components/features/documents/TMDcmtBlog.js +21 -33
  19. package/lib/components/features/documents/TMDcmtForm.d.ts +17 -3
  20. package/lib/components/features/documents/TMDcmtForm.js +205 -46
  21. package/lib/components/features/documents/TMDcmtTasks.d.ts +13 -0
  22. package/lib/components/features/documents/TMDcmtTasks.js +24 -0
  23. package/lib/components/features/documents/TMDragDropOverlay.js +2 -1
  24. package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +8 -1
  25. package/lib/components/features/documents/TMMasterDetailDcmts.js +6 -6
  26. package/lib/components/features/documents/TMRelationViewer.d.ts +53 -3
  27. package/lib/components/features/documents/TMRelationViewer.js +232 -85
  28. package/lib/components/features/search/TMSearch.d.ts +10 -1
  29. package/lib/components/features/search/TMSearch.js +14 -5
  30. package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
  31. package/lib/components/features/search/TMSearchQueryPanel.js +36 -7
  32. package/lib/components/features/search/TMSearchResult.d.ts +10 -1
  33. package/lib/components/features/search/TMSearchResult.js +140 -422
  34. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +2 -2
  35. package/lib/components/features/search/TMSearchResultsMenuItems.js +33 -8
  36. package/lib/components/features/tasks/TMTaskForm.d.ts +38 -0
  37. package/lib/components/features/tasks/TMTaskForm.js +386 -0
  38. package/lib/components/features/tasks/TMTasksAgenda.d.ts +17 -0
  39. package/lib/components/features/tasks/TMTasksAgenda.js +107 -0
  40. package/lib/components/features/tasks/TMTasksCalendar.d.ts +21 -0
  41. package/lib/components/features/tasks/TMTasksCalendar.js +240 -0
  42. package/lib/components/features/tasks/TMTasksHeader.d.ts +14 -0
  43. package/lib/components/features/tasks/TMTasksHeader.js +37 -0
  44. package/lib/components/features/tasks/TMTasksPanelContent.d.ts +20 -0
  45. package/lib/components/features/tasks/TMTasksPanelContent.js +65 -0
  46. package/lib/components/features/tasks/TMTasksUtils.d.ts +132 -0
  47. package/lib/components/features/tasks/TMTasksUtils.js +634 -0
  48. package/lib/components/features/tasks/TMTasksUtilsView.d.ts +39 -0
  49. package/lib/components/features/tasks/TMTasksUtilsView.js +118 -0
  50. package/lib/components/features/tasks/TMTasksView.d.ts +40 -0
  51. package/lib/components/features/tasks/TMTasksView.js +560 -0
  52. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +3 -1
  53. package/lib/components/features/workflow/TMWorkflowPopup.js +19 -6
  54. package/lib/components/features/workflow/diagram/RecipientList.js +4 -3
  55. package/lib/components/forms/Login/Chooser.js +1 -1
  56. package/lib/components/forms/TMChooserForm.d.ts +1 -1
  57. package/lib/components/forms/TMChooserForm.js +2 -2
  58. package/lib/components/grids/TMBlogAttachments.d.ts +42 -0
  59. package/lib/components/grids/TMBlogAttachments.js +43 -0
  60. package/lib/components/grids/TMBlogHeader.d.ts +31 -0
  61. package/lib/components/grids/TMBlogHeader.js +41 -0
  62. package/lib/components/grids/{TMBlogs.d.ts → TMBlogsPost.d.ts} +42 -58
  63. package/lib/components/grids/TMBlogsPost.js +628 -0
  64. package/lib/components/grids/{TMBlogsUtils.d.ts → TMBlogsPostUtils.d.ts} +61 -47
  65. package/lib/components/grids/{TMBlogsUtils.js → TMBlogsPostUtils.js} +146 -124
  66. package/lib/components/index.d.ts +14 -1
  67. package/lib/components/index.js +15 -1
  68. package/lib/components/layout/panelManager/TMPanelManagerContext.js +7 -0
  69. package/lib/components/settings/SettingsAppearance.js +8 -0
  70. package/lib/components/viewers/TMTidViewer.js +20 -2
  71. package/lib/css/tm-sdkui.css +1 -1
  72. package/lib/helper/SDKUI_Globals.d.ts +4 -1
  73. package/lib/helper/SDKUI_Globals.js +10 -1
  74. package/lib/helper/SDKUI_Localizator.d.ts +62 -4
  75. package/lib/helper/SDKUI_Localizator.js +618 -25
  76. package/lib/helper/TMCustomSearchBar.d.ts +8 -0
  77. package/lib/helper/TMCustomSearchBar.js +54 -0
  78. package/lib/helper/TMIcons.d.ts +2 -0
  79. package/lib/helper/TMIcons.js +6 -0
  80. package/lib/helper/TMImageLibrary.d.ts +3 -2
  81. package/lib/helper/TMImageLibrary.js +230 -230
  82. package/lib/helper/TMToppyMessage.d.ts +7 -0
  83. package/lib/helper/TMToppyMessage.js +42 -0
  84. package/lib/helper/TMUtils.d.ts +10 -1
  85. package/lib/helper/TMUtils.js +42 -1
  86. package/lib/helper/dcmtsHelper.d.ts +2 -0
  87. package/lib/helper/dcmtsHelper.js +18 -0
  88. package/lib/helper/helpers.js +1 -0
  89. package/lib/helper/index.d.ts +1 -0
  90. package/lib/helper/index.js +1 -0
  91. package/lib/hooks/useRelatedDocuments.d.ts +72 -0
  92. package/lib/hooks/useRelatedDocuments.js +655 -0
  93. package/lib/index.d.ts +1 -0
  94. package/lib/index.js +1 -0
  95. package/lib/ts/types.d.ts +14 -0
  96. package/lib/ts/types.js +15 -0
  97. package/lib/utils/theme.d.ts +1 -0
  98. package/lib/utils/theme.js +1 -0
  99. package/package.json +7 -7
  100. package/lib/components/grids/TMBlogs.js +0 -721
  101. package/lib/stories/TMButton.stories.d.ts +0 -4
  102. package/lib/stories/TMButton.stories.js +0 -29
  103. package/lib/stories/TMDataGrid.stories.d.ts +0 -9
  104. package/lib/stories/TMDataGrid.stories.js +0 -310
  105. package/lib/stories/TMHtmlContentDisplay.stories.d.ts +0 -6
  106. package/lib/stories/TMHtmlContentDisplay.stories.js +0 -45
  107. package/lib/stories/TMHtmlEditor.stories.d.ts +0 -6
  108. package/lib/stories/TMHtmlEditor.stories.js +0 -49
  109. package/lib/stories/TMIcons.stories.d.ts +0 -4
  110. package/lib/stories/TMIcons.stories.js +0 -13
  111. package/lib/stories/TMSDKUI_Localizator.stories.d.ts +0 -4
  112. package/lib/stories/TMSDKUI_Localizator.stories.js +0 -123
  113. package/lib/stories/TMStoriesUtils.d.ts +0 -1
  114. package/lib/stories/TMStoriesUtils.js +0 -10
  115. package/lib/stories/TMUserAvatar.stories.d.ts +0 -6
  116. package/lib/stories/TMUserAvatar.stories.js +0 -20
@@ -0,0 +1,628 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useCallback, useEffect, useRef, useState } from "react";
3
+ import { LayoutModes, ResultTypes, SDK_Globals, WorkingGroupEngine } from "@topconsultnpm/sdk-ts";
4
+ import { ContextMenu } from "devextreme-react";
5
+ import { SDKUI_Localizator, Globalization, getExceptionMessage, TMConditionalWrapper } from "../../helper";
6
+ import TMToppyMessage from "../../helper/TMToppyMessage";
7
+ import { useDcmtOperations } from "../../hooks/useDcmtOperations";
8
+ import { DownloadTypes } from "../../ts";
9
+ import { TMColors } from "../../utils/theme";
10
+ import ShowAlert from "../base/TMAlert";
11
+ import { TMMessageBoxManager, ButtonNames } from "../base/TMPopUp";
12
+ import TMTooltip from "../base/TMTooltip";
13
+ import { TMLayoutWaitingContainer } from "../base/TMWaitPanel";
14
+ import TMHtmlContentDisplay from "../editors/TMHtmlContentDisplay";
15
+ import TMDcmtForm from "../features/documents/TMDcmtForm";
16
+ import { TMResultManager } from "../forms/TMResultDialog";
17
+ import { isHeaderFullyHidden, TMBlogsFilterCategoryId, getDcmtTypeDescriptor, findFileItemByDraftID, BlogPostTitle, NewBadge } from "./TMBlogsPostUtils";
18
+ import TMBlogAttachments from "./TMBlogAttachments";
19
+ import TMBlogHeader from "./TMBlogHeader";
20
+ let localAbortController = new AbortController();
21
+ const TMBlogsPost = (props) => {
22
+ const { scrollToSelected, id, posts, displayMode = "stacked", height = "100%", width = "100%", scrollToBottom = true, header, showExtendedAttachments = true, treeFs, draftLatestInfoMap, archivedDocumentMap, context, contextMenuParams = {
23
+ isShowHideFilterEnabled: true,
24
+ isShowHideIDEnaled: true,
25
+ isCommentEnabled: false,
26
+ isDownloadAttachmentEnabled: false,
27
+ isViewEditMetadata: false,
28
+ isDeleteEnabled: false,
29
+ isCopyToClipboardEnabled: false,
30
+ isRestoreEnabled: false,
31
+ isRefreshEnabled: false,
32
+ isCreateContextualTask: false,
33
+ }, showFloatingCommentButton = false, showCommentFormCallback, showTaskFormCallback, refreshCallback, showId, setShowId, refreshHomePageNews, markBlogAsRead, externalBlogPost, resetExternalBlogPost, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, } = props;
34
+ const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync } = useDcmtOperations();
35
+ const bottomRef = useRef(null);
36
+ const containerRef = useRef(null);
37
+ // State to manage the layout mode of the document form
38
+ const [layoutMode, setLayoutMode] = useState("extended");
39
+ const COMPACT_WIDTH_THRESHOLD = 465;
40
+ const COMPACT_HEIGHT_THRESHOLD = 500;
41
+ // State to store an array of blog posts, which can be either BlogPost or HomeBlogPost type
42
+ const [blogPosts, setBlogPosts] = useState([]);
43
+ // State to store the first unread post
44
+ const [firstUnreadPost, setFirstUnreadPost] = useState(undefined);
45
+ // State to manage the focused file
46
+ const [dcmtTypeDescriptors, setDcmtTypeDescriptors] = useState(new Map());
47
+ const [currentHeader, setCurrentHeader] = useState(header);
48
+ const [isHeaderHidden, setIsHeaderHidden] = useState(isHeaderFullyHidden(currentHeader));
49
+ const [localShowId, setLocalShowId] = useState(false);
50
+ // Filter states
51
+ const [searchText, setSearchText] = useState('');
52
+ const [postsToShow, setPostsToShow] = useState(30);
53
+ const [categoryIdDataSource, setCategoryIdDataSource] = useState([]);
54
+ const [appliedCategoryIdFilters, setAppliedCategoryIdFilters] = useState([TMBlogsFilterCategoryId.PublishedBlogs]);
55
+ // State to manage the selected item
56
+ const [selectedItem, setSelectedItem] = useState([]);
57
+ // State to manage the focused item
58
+ const [focusedItem, setFocusedItem] = useState(undefined);
59
+ // State to manage the focused file
60
+ const [focusedAttachment, setFocusedAttachment] = useState(undefined);
61
+ // State to manage show selected file
62
+ const [dcmtForm, setDcmtForm] = useState({ show: false, dcmt: undefined });
63
+ const [anchorEl, setAnchorEl] = useState(null);
64
+ const [menuItems, setMenuItems] = useState([]);
65
+ const [localShowWaitPanel, setLocalShowWaitPanel] = useState(false);
66
+ const [localWaitPanelTitle, setLocalWaitPanelTitle] = useState('');
67
+ const [localShowPrimary, setLocalShowPrimary] = useState(false);
68
+ const [localWaitPanelTextPrimary, setLocalWaitPanelTextPrimary] = useState('');
69
+ const [localWaitPanelValuePrimary, setLocalWaitPanelValuePrimary] = useState(0);
70
+ const [localWaitPanelMaxValuePrimary, setLocalWaitPanelMaxValuePrimary] = useState(0);
71
+ const handleFocusedItem = (item) => {
72
+ setFocusedAttachment(undefined);
73
+ setFocusedItem(item);
74
+ };
75
+ const handleSelectedItem = (item) => {
76
+ setFocusedAttachment(undefined);
77
+ if (markBlogAsRead && item.length > 0) {
78
+ if (item[0].customData1 === 1)
79
+ markBlogAsRead(item[0]);
80
+ }
81
+ setSelectedItem(item ? item : []);
82
+ };
83
+ const handleFocusedAttachment = (attachment) => {
84
+ setFocusedAttachment(attachment);
85
+ };
86
+ useEffect(() => {
87
+ if (externalBlogPost && externalBlogPost.id) {
88
+ const foundPost = blogPosts.find(post => post.id === externalBlogPost.id);
89
+ if (foundPost) {
90
+ resetExternalBlogPost?.();
91
+ handleSelectedItem([foundPost]);
92
+ // Retrieve the DOM element using the ID built with the pattern: {id}-blogpost-{id of the first selected item}
93
+ const element = document.getElementById(`${id}-blogpost-${foundPost.id}`);
94
+ // Check if the element was found in the DOM
95
+ if (element) {
96
+ // Scroll the view to bring the element to the center of the screen
97
+ element.scrollIntoView({ block: "nearest", inline: "nearest" });
98
+ }
99
+ }
100
+ }
101
+ }, [externalBlogPost, blogPosts]);
102
+ useEffect(() => {
103
+ if (!containerRef.current)
104
+ return;
105
+ const ro = new ResizeObserver(entries => {
106
+ for (let entry of entries) {
107
+ const { width, height } = entry.contentRect;
108
+ // Compact mode triggers if width OR height is below threshold
109
+ if ((width < COMPACT_WIDTH_THRESHOLD || height < COMPACT_HEIGHT_THRESHOLD) && layoutMode !== "compact") {
110
+ setLayoutMode("compact");
111
+ }
112
+ else if (width >= COMPACT_WIDTH_THRESHOLD && height >= COMPACT_HEIGHT_THRESHOLD && layoutMode !== "extended") {
113
+ setLayoutMode("extended");
114
+ }
115
+ }
116
+ });
117
+ ro.observe(containerRef.current);
118
+ return () => ro.disconnect();
119
+ }, [layoutMode]);
120
+ useEffect(() => {
121
+ const fetchDcmtTypeDescriptor = async () => {
122
+ const descriptors = await getDcmtTypeDescriptor(posts);
123
+ setDcmtTypeDescriptors(descriptors);
124
+ };
125
+ if (showExtendedAttachments) {
126
+ fetchDcmtTypeDescriptor();
127
+ }
128
+ const publishedBlogssLength = posts.filter(newsFeed => newsFeed.isSys !== 1 && newsFeed.isDel !== 1).length;
129
+ const systemBlogsLength = posts.filter(newsFeed => newsFeed.isSys === 1).length;
130
+ const deletedBlogsLength = posts.filter(newsFeed => newsFeed.isDel === 1).length;
131
+ setCategoryIdDataSource([
132
+ {
133
+ id: TMBlogsFilterCategoryId.PublishedBlogs,
134
+ label: SDKUI_Localizator.Active + " (" + publishedBlogssLength + ")",
135
+ value: SDKUI_Localizator.Active,
136
+ },
137
+ {
138
+ id: TMBlogsFilterCategoryId.SystemBlogs,
139
+ label: SDKUI_Localizator.OfSystem + " (" + systemBlogsLength + ")",
140
+ value: SDKUI_Localizator.OfSystem,
141
+ },
142
+ {
143
+ id: TMBlogsFilterCategoryId.DeletedBlogs,
144
+ label: SDKUI_Localizator.Deleted + " (" + deletedBlogsLength + ")",
145
+ value: SDKUI_Localizator.Deleted,
146
+ },
147
+ ]);
148
+ }, [posts]);
149
+ useEffect(() => {
150
+ setIsHeaderHidden(isHeaderFullyHidden(currentHeader));
151
+ }, [currentHeader]);
152
+ // Effect hook to update blogPosts state whenever the posts prop changes
153
+ useEffect(() => {
154
+ // Helper function to check if a blog post is a system blog
155
+ const isActiveBlog = (blog) => blog.isSys !== 1 && blog.isDel !== 1;
156
+ // Helper function to check if a blog post is a system blog
157
+ const isSystemBlog = (blog) => blog.isSys === 1;
158
+ // Helper function to check if a blog post is marked as deleted
159
+ const isDeletedBlog = (blog) => blog.isDel === 1;
160
+ // Create a mapping of filter category IDs to their corresponding filter functions
161
+ const filters = {
162
+ [TMBlogsFilterCategoryId.PublishedBlogs]: isActiveBlog, // Map PublishedBlogs filter to the isActiveBlog function
163
+ [TMBlogsFilterCategoryId.SystemBlogs]: isSystemBlog, // Map SystemBlogs filter to the isSystemBlog function
164
+ [TMBlogsFilterCategoryId.DeletedBlogs]: isDeletedBlog, // Map DeletedBlogs filter to the isDeletedBlog function
165
+ };
166
+ // Function to apply filters to a single blog post
167
+ const applyFilters = (newsFeed) => {
168
+ // Check if the blog post matches the search text (case insensitive)
169
+ if (searchText && searchText.trim().length > 0 && !(newsFeed.ownerName?.toLowerCase().includes(searchText.toLowerCase().trim())
170
+ || newsFeed.description?.toLowerCase().includes(searchText.toLowerCase().trim())
171
+ || newsFeed.id?.toString()?.toLowerCase().includes(searchText.toLowerCase().trim())
172
+ || Globalization.getDateTimeDisplayValue(newsFeed.creationTime)?.toString()?.toLowerCase().includes(searchText.toLowerCase().trim())
173
+ || newsFeed.header?.toString()?.toLowerCase().includes(searchText.toLowerCase().trim())
174
+ || (newsFeed.attachments && newsFeed.attachments?.length > 0 && newsFeed.attachments.some((attachment) => findFileItemByDraftID(treeFs, attachment.draftID)?.name?.toLowerCase().includes(searchText.toLowerCase().trim())))
175
+ || (newsFeed.attachments && newsFeed.attachments?.length > 0 && newsFeed.attachments.some((attachment) => attachment.tid && dcmtTypeDescriptors.get(attachment.tid)?.name?.toLowerCase().includes(searchText.toLowerCase().trim()))))) {
176
+ return false; // If the blog post doesn't match the search text, filter it out
177
+ }
178
+ // If no global filters are applied, exclude system and deleted blogs by default
179
+ if (appliedCategoryIdFilters.length === 0) {
180
+ return !isActiveBlog(newsFeed) && !isSystemBlog(newsFeed) && !isDeletedBlog(newsFeed);
181
+ }
182
+ // Check if the blog post matches any of the applied global filters
183
+ const matchesFilter = appliedCategoryIdFilters.some((filter) => filters[filter](newsFeed));
184
+ // Return true if it matches any applied filter or if it's neither a system nor a deleted blog
185
+ return matchesFilter || (!isActiveBlog(newsFeed) && !isSystemBlog(newsFeed) && !isDeletedBlog(newsFeed));
186
+ };
187
+ // Apply the filter logic to the complete list of blogs
188
+ let filteredBlogs = posts.filter(applyFilters);
189
+ // Limit the results to the most recent postsToShow number of posts if the condition is true
190
+ filteredBlogs = filteredBlogs.slice(-postsToShow);
191
+ // Update the state with the filtered and limited list of blogs
192
+ setBlogPosts(filteredBlogs);
193
+ // Update the first unread post state
194
+ setFirstUnreadPost(filteredBlogs.find(blog => blog.customData1 === 1));
195
+ }, [posts, appliedCategoryIdFilters, searchText, postsToShow]);
196
+ useEffect(() => {
197
+ if (!scrollToSelected)
198
+ return;
199
+ if (blogPosts.length === 0)
200
+ return;
201
+ // Check if there is at least one selected item in the selectedItem array
202
+ if (selectedItem.length > 0) {
203
+ // Retrieve the DOM element using the ID built with the pattern: {id}-blogpost-{id of the first selected item}
204
+ const element = document.getElementById(`${id}-blogpost-${selectedItem[0].id}`);
205
+ // Check if the element was found in the DOM
206
+ if (element) {
207
+ // Scroll the view to bring the element to the center of the screen
208
+ element.scrollIntoView({ block: "nearest", inline: "nearest" });
209
+ }
210
+ }
211
+ else {
212
+ // If no items are selected, check if auto-scroll is enabled and if the bottom reference exists
213
+ if (scrollToBottom && bottomRef.current)
214
+ bottomRef.current.scrollIntoView({ block: "nearest", inline: "nearest" });
215
+ }
216
+ }, [scrollToBottom, blogPosts, scrollToSelected, selectedItem]);
217
+ useEffect(() => {
218
+ if (showId !== undefined)
219
+ setLocalShowId(showId);
220
+ }, [showId]);
221
+ useEffect(() => {
222
+ if (setShowId)
223
+ setShowId(localShowId);
224
+ }, [localShowId]);
225
+ const toggleHeaderClick = () => {
226
+ setCurrentHeader(prevState => {
227
+ if (prevState === undefined) {
228
+ return {
229
+ showViewMode: true,
230
+ showFilters: true,
231
+ showSearchBar: true,
232
+ showPostsDropDown: true,
233
+ };
234
+ }
235
+ else {
236
+ return {
237
+ showViewMode: !prevState.showViewMode,
238
+ showFilters: !prevState.showFilters,
239
+ showSearchBar: !prevState.showSearchBar,
240
+ showPostsDropDown: !prevState.showPostsDropDown,
241
+ };
242
+ }
243
+ });
244
+ };
245
+ const refresh = async () => {
246
+ if (refreshCallback)
247
+ await refreshCallback();
248
+ };
249
+ // Handlers for mouse events
250
+ const handleMouseEnter = (item) => handleFocusedItem(item);
251
+ const handleMouseLeave = () => handleFocusedItem(undefined);
252
+ const handleClick = (item) => handleSelectedItem([item]);
253
+ const handleDoubleClick = async (blogPost) => {
254
+ if (handleNavigateToWGs && id && blogPost.classID === 'WG')
255
+ handleNavigateToWGs(blogPost);
256
+ if (handleNavigateToDossiers && id && blogPost.classID === 'DS')
257
+ handleNavigateToDossiers(blogPost);
258
+ };
259
+ const handleKeyDown = (event) => {
260
+ if (!selectedItem.length)
261
+ return;
262
+ const currentIndex = blogPosts.findIndex(i => i.id === selectedItem[0].id);
263
+ if (currentIndex === -1)
264
+ return;
265
+ let newIndex = currentIndex;
266
+ switch (event.key) {
267
+ case "ArrowDown":
268
+ if (currentIndex < blogPosts.length - 1) {
269
+ newIndex = currentIndex + 1;
270
+ handleSelectedItem([blogPosts[newIndex]]);
271
+ }
272
+ event.preventDefault();
273
+ break;
274
+ case "ArrowUp":
275
+ if (currentIndex > 0) {
276
+ newIndex = currentIndex - 1;
277
+ handleSelectedItem([blogPosts[newIndex]]);
278
+ }
279
+ event.preventDefault();
280
+ break;
281
+ case "Enter":
282
+ const item = blogPosts[currentIndex];
283
+ const classItemID = item.classID;
284
+ if (handleNavigateToWGs && item.id && classItemID === 'WG') {
285
+ handleNavigateToWGs(item);
286
+ }
287
+ if (handleNavigateToDossiers && item.id && classItemID === 'DS') {
288
+ handleNavigateToDossiers(item);
289
+ }
290
+ break;
291
+ default:
292
+ break;
293
+ }
294
+ // Scrolla automaticamente all'elemento selezionato
295
+ const element = document.getElementById(`${id}-blogpost-${blogPosts[newIndex].id}`);
296
+ if (element) {
297
+ element.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
298
+ }
299
+ };
300
+ // Unfollow callback
301
+ const unFollowCallback = async (currentBlog, classId) => {
302
+ const title = SDKUI_Localizator.Unfollow;
303
+ const header = currentBlog.header ?? undefined;
304
+ const message = header ? SDKUI_Localizator.UnfollowSelectedItem.replaceParams(header) : SDKUI_Localizator.Unfollow + "?";
305
+ TMMessageBoxManager.show({
306
+ title, message, buttons: [ButtonNames.YES, ButtonNames.NO],
307
+ confirmOnEnter: true,
308
+ onButtonClick: async (e) => {
309
+ if (e !== ButtonNames.YES)
310
+ return;
311
+ let result = [];
312
+ switch (classId) {
313
+ case 'WG':
314
+ const workingGroupEngine = new WorkingGroupEngine(SDK_Globals.tmSession);
315
+ await workingGroupEngine.FollowAddOrRemoveAsync(currentBlog.id, true).then(async () => {
316
+ result.push({ rowIndex: currentBlog.id, id1: currentBlog.id, id2: currentBlog.id, description: SDKUI_Localizator.OperationSuccess, resultType: ResultTypes.SUCCESS });
317
+ refreshHomePageNews?.();
318
+ })
319
+ .catch((err) => {
320
+ result.push({ rowIndex: currentBlog.id, id1: currentBlog.id, id2: currentBlog.id, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
321
+ });
322
+ break;
323
+ default:
324
+ ShowAlert({ message: 'TODO', mode: 'warning', title: SDKUI_Localizator.Unfollow, duration: 3000 });
325
+ break;
326
+ }
327
+ TMResultManager.show(result, SDKUI_Localizator.Unfollow, "ID", undefined);
328
+ }
329
+ });
330
+ };
331
+ // Copy to clipboard callback
332
+ const copyInClipboard = (blog) => {
333
+ if (blog === undefined)
334
+ return;
335
+ const { id, ownerName, creationTime, description } = blog;
336
+ const formattedText = `${ownerName} (${Globalization.getDateTimeDisplayValue(creationTime)}):\n${description}`;
337
+ let result = [];
338
+ window.navigator.clipboard.writeText(formattedText)
339
+ .then(() => {
340
+ result = [{ rowIndex: id ?? 1, id1: id ?? 1, id2: id ?? 1, description: SDKUI_Localizator.UpdateCompletedSuccessfully, resultType: ResultTypes.SUCCESS }];
341
+ })
342
+ .catch(err => {
343
+ result = [{ rowIndex: id ?? 1, id1: id ?? 1, id2: id ?? 1, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) }];
344
+ })
345
+ .finally(() => {
346
+ TMResultManager.show(result, SDKUI_Localizator.CopyToClipboard, "ID", undefined);
347
+ });
348
+ };
349
+ // Delete or undelete comment callback
350
+ const deleteOrUndeleteCommentCallback = async (currentBlog, del) => {
351
+ if (currentBlog === undefined || currentBlog.id === undefined)
352
+ return Promise.resolve();
353
+ const msg = del ? SDKUI_Localizator.DeleteComment : SDKUI_Localizator.RestoreComment;
354
+ TMMessageBoxManager.show({
355
+ title: del ? SDKUI_Localizator.Delete : SDKUI_Localizator.Restore, message: msg, buttons: [ButtonNames.YES, ButtonNames.NO],
356
+ onButtonClick: async (e) => {
357
+ if (e !== ButtonNames.YES)
358
+ return;
359
+ if (currentBlog === undefined || currentBlog.id === undefined || context === undefined || context.object === undefined
360
+ || (context.engine === 'WorkingGroupEngine' && !context.object.id)
361
+ || (context.engine === 'SearchEngine' && (!context.object.tid || !context.object.did))
362
+ || (context.engine === 'DossierEngine' && !context.object.id)) {
363
+ return Promise.resolve();
364
+ }
365
+ setLocalWaitPanelTitle(del ? SDKUI_Localizator.Delete : SDKUI_Localizator.Restore);
366
+ setLocalShowWaitPanel(true);
367
+ setLocalShowPrimary(true);
368
+ localAbortController = new AbortController();
369
+ let result = [];
370
+ let i = 0;
371
+ setLocalWaitPanelMaxValuePrimary(1);
372
+ if (localAbortController.signal.aborted) {
373
+ result.push({ rowIndex: i, id1: currentBlog.id, id2: currentBlog.id, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborate ${i}` });
374
+ }
375
+ else {
376
+ setLocalWaitPanelTextPrimary(del ? SDKUI_Localizator.Delete : SDKUI_Localizator.Restore);
377
+ if (context.engine === 'WorkingGroupEngine' && context.object.id) {
378
+ const workingGroupEngine = new WorkingGroupEngine(SDK_Globals.tmSession);
379
+ await workingGroupEngine.BlogPostDeleteOrUndeleteAsync(context.object.id, currentBlog.id, del)
380
+ .then(() => {
381
+ handleSelectedItem([]);
382
+ refresh();
383
+ })
384
+ .catch((err) => {
385
+ result.push({ rowIndex: 1, id1: context.object?.id, id2: context.object?.id, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
386
+ TMResultManager.show(result, del ? SDKUI_Localizator.Delete : SDKUI_Localizator.Restore, "ID", undefined);
387
+ });
388
+ }
389
+ else if (context.engine === 'SearchEngine' && context.object.tid && context.object.did) {
390
+ const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
391
+ await searchEngine.BlogPostDeleteOrUndeleteAsync(context.object.tid, context.object.did, currentBlog.id, del)
392
+ .then(() => {
393
+ handleSelectedItem([]);
394
+ refresh();
395
+ })
396
+ .catch((err) => {
397
+ result.push({ rowIndex: 1, id1: context.object?.tid, id2: context.object?.did, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
398
+ TMResultManager.show(result, del ? SDKUI_Localizator.Delete : SDKUI_Localizator.Restore, "ID", undefined);
399
+ });
400
+ }
401
+ else if (context.engine === 'DossierEngine' && context.object.id) {
402
+ const dossierEngine = SDK_Globals.tmSession?.NewDossierEngine();
403
+ await dossierEngine.BlogPostDeleteOrUndeleteAsync(context.object.id, currentBlog.id, del)
404
+ .then(() => {
405
+ handleSelectedItem([]);
406
+ refresh();
407
+ })
408
+ .catch((err) => {
409
+ result.push({ rowIndex: 1, id1: context.object?.id, id2: context.object?.id, resultType: ResultTypes.ERROR, description: getExceptionMessage(err) });
410
+ TMResultManager.show(result, del ? SDKUI_Localizator.Delete : SDKUI_Localizator.Restore, "ID", undefined);
411
+ });
412
+ }
413
+ }
414
+ setLocalWaitPanelTextPrimary('');
415
+ setLocalWaitPanelMaxValuePrimary(0);
416
+ setLocalWaitPanelValuePrimary(0);
417
+ setLocalShowWaitPanel(false);
418
+ }
419
+ });
420
+ };
421
+ // Open document form
422
+ const openDcmtForm = (dcmtInfo) => {
423
+ setDcmtForm({ show: true, dcmt: dcmtInfo });
424
+ };
425
+ const closeDcmtForm = () => {
426
+ setDcmtForm({ show: false, dcmt: undefined });
427
+ };
428
+ // Context menu handler
429
+ const onContextMenu = (event) => {
430
+ if (!event)
431
+ return;
432
+ event.preventDefault();
433
+ const userId = SDK_Globals.tmSession?.SessionDescr?.userID;
434
+ const menuItems = [];
435
+ const targetItem = focusedItem ?? (selectedItem.length === 1 ? selectedItem[0] : undefined);
436
+ const isGroupArchived = Boolean(context && context.engine === 'WorkingGroupEngine' && context.object && context.object.customData1 === 1);
437
+ const isNotOwner = targetItem && targetItem.ownerID !== userId;
438
+ const isDeleted = targetItem && (targetItem.isDel !== undefined && targetItem.isDel !== 0);
439
+ const classId = targetItem ? targetItem.classID : undefined;
440
+ if (contextMenuParams.isDownloadAttachmentEnabled && focusedAttachment !== undefined) {
441
+ menuItems.push({
442
+ icon: "download",
443
+ text: 'Download',
444
+ onClick: async () => {
445
+ await downloadDcmtsAsync([
446
+ { TID: focusedAttachment.TID, DID: focusedAttachment.DID, fileName: focusedAttachment.fileName }
447
+ ], DownloadTypes.Dcmt, "download");
448
+ },
449
+ });
450
+ }
451
+ if (contextMenuParams.isViewEditMetadata && focusedAttachment !== undefined) {
452
+ menuItems.push({
453
+ icon: 'eyeopen',
454
+ text: SDKUI_Localizator.OpenForm,
455
+ onClick: () => openDcmtForm(focusedAttachment),
456
+ });
457
+ }
458
+ if (contextMenuParams.isCommentEnabled && showCommentFormCallback !== undefined) {
459
+ menuItems.push({
460
+ icon: 'chat',
461
+ text: SDKUI_Localizator.Comment,
462
+ disabled: isGroupArchived ? true : false,
463
+ onClick: () => {
464
+ showCommentFormCallback(focusedAttachment);
465
+ },
466
+ beginGroup: true
467
+ });
468
+ }
469
+ if (targetItem && contextMenuParams.isDeleteEnabled) {
470
+ menuItems.push({
471
+ icon: "trash",
472
+ text: SDKUI_Localizator.Delete,
473
+ visible: contextMenuParams.isDeleteEnabled,
474
+ onClick: () => deleteOrUndeleteCommentCallback(targetItem, true),
475
+ disabled: isNotOwner || isDeleted || isGroupArchived,
476
+ });
477
+ }
478
+ if (targetItem && contextMenuParams.isRestoreEnabled) {
479
+ menuItems.push({
480
+ icon: "undo",
481
+ text: SDKUI_Localizator.Restore,
482
+ onClick: () => deleteOrUndeleteCommentCallback(targetItem, false),
483
+ disabled: isNotOwner || (targetItem && (targetItem.isDel === undefined || targetItem.isDel === 0)),
484
+ });
485
+ }
486
+ if (targetItem && contextMenuParams.isCopyToClipboardEnabled) {
487
+ menuItems.push({
488
+ text: SDKUI_Localizator.CopyToClipboard,
489
+ icon: 'copy',
490
+ disabled: false,
491
+ onClick: () => { copyInClipboard(targetItem); }
492
+ });
493
+ }
494
+ if (contextMenuParams.isCreateContextualTask && showTaskFormCallback) {
495
+ menuItems.push({
496
+ text: SDKUI_Localizator.CreateContextualTask,
497
+ icon: 'plus',
498
+ onClick: () => showTaskFormCallback(),
499
+ disabled: isGroupArchived ? true : false,
500
+ beginGroup: true
501
+ });
502
+ }
503
+ if (targetItem && Boolean(classId && classId === 'WG')) {
504
+ menuItems.push({
505
+ icon: "eyeclose",
506
+ text: SDKUI_Localizator.Unfollow,
507
+ onClick: () => targetItem && classId && unFollowCallback(targetItem, classId),
508
+ });
509
+ }
510
+ if (contextMenuParams.isShowHideFilterEnabled) {
511
+ menuItems.push({
512
+ icon: isHeaderHidden ? 'eyeopen' : 'eyeclose',
513
+ text: isHeaderHidden ? SDKUI_Localizator.ShowFilters : SDKUI_Localizator.HideFilters,
514
+ onClick: toggleHeaderClick,
515
+ disabled: false,
516
+ beginGroup: true
517
+ });
518
+ }
519
+ if (contextMenuParams.isShowHideIDEnaled) {
520
+ menuItems.push({
521
+ icon: localShowId ? 'eyeclose' : 'eyeopen',
522
+ onClick: () => setLocalShowId(prevShowId => !prevShowId),
523
+ text: localShowId ? SDKUI_Localizator.ID_Hide : SDKUI_Localizator.ID_Show,
524
+ disabled: false,
525
+ });
526
+ }
527
+ if (contextMenuParams.isRefreshEnabled && refreshCallback !== undefined) {
528
+ menuItems.push({
529
+ icon: "refresh",
530
+ text: SDKUI_Localizator.Refresh,
531
+ onClick: refresh,
532
+ disabled: false,
533
+ });
534
+ }
535
+ if (menuItems.length > 0) {
536
+ setAnchorEl(event.currentTarget);
537
+ setMenuItems(menuItems);
538
+ }
539
+ };
540
+ const handlePostsToShowChange = (value) => {
541
+ setPostsToShow(value);
542
+ };
543
+ const handleSearchChange = (value) => {
544
+ handleSelectedItem([]);
545
+ setSearchText(value);
546
+ };
547
+ const closeContextMenu = useCallback(() => {
548
+ setAnchorEl(null);
549
+ }, []);
550
+ return _jsx("div", { ref: containerRef, style: { height: height, width: width }, children: _jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: _jsxs(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: localShowWaitPanel, showWaitPanelPrimary: localShowPrimary, waitPanelTitle: localWaitPanelTitle, waitPanelTextPrimary: localWaitPanelTextPrimary, waitPanelValuePrimary: localWaitPanelValuePrimary, waitPanelMaxValuePrimary: localWaitPanelMaxValuePrimary, isCancelable: true, abortController: localAbortController, children: [_jsx(TMBlogHeader, { isVisible: currentHeader !== undefined && !isHeaderHidden, layoutMode: layoutMode, height: layoutMode === 'extended' ? "40px" : "70px", width: "100%", allPosts: posts, postsToShow: postsToShow, onPostsToShowChange: handlePostsToShowChange, categoryIdDataSource: categoryIdDataSource, appliedCategoryIdFilters: appliedCategoryIdFilters, setAppliedCategoryIdFilters: setAppliedCategoryIdFilters, searchText: searchText, onSearchChange: handleSearchChange }), _jsxs("div", { style: {
551
+ width: "100%",
552
+ height: currentHeader !== undefined && !isHeaderHidden ? `calc(100% - ${layoutMode === 'extended' ? "40px" : "70px"})` : "100%",
553
+ }, children: [blogPosts.length === 0 && _jsx(TMToppyMessage, { message: searchText.length > 0 ? SDKUI_Localizator.NoMessagesFound : SDKUI_Localizator.NoMessages }), blogPosts.length > 0 && _jsxs("div", { tabIndex: 0, onKeyDown: handleKeyDown, onContextMenu: onContextMenu, style: {
554
+ height: '100%',
555
+ width: '100%',
556
+ outline: "none",
557
+ display: "flex",
558
+ flexDirection: "column",
559
+ rowGap: "10px",
560
+ overflowY: "auto",
561
+ padding: "5px",
562
+ backgroundColor: displayMode === 'stacked' ? "#e0e0e0" : "#fff"
563
+ }, children: [blogPosts.map((blogPost) => {
564
+ const isSelected = selectedItem.some(item => item.id === blogPost.id);
565
+ const isFocused = focusedItem?.id === blogPost.id;
566
+ const isHomeBlogPost = Boolean(blogPost.header?.trim());
567
+ const isNew = Boolean(blogPost.newPosts ?? blogPost.customData1);
568
+ const isOwnComment = blogPost.ownerID === SDK_Globals.tmSession?.SessionDescr?.userID;
569
+ const isSys = blogPost.isSys === 1;
570
+ return _jsxs(React.Fragment, { children: [(firstUnreadPost && blogPost.id === firstUnreadPost.id) && (_jsxs("div", { style: { display: 'flex', alignItems: 'center', width: '100%', color: TMColors.primary, fontWeight: '600', fontSize: '0.9rem', userSelect: 'none', marginTop: "12px", marginBottom: "12px" }, children: [_jsx("hr", { style: { flex: 1, border: 'none', borderTop: `1px solid ${TMColors.primary}`, marginRight: '15px' } }), _jsx("span", { children: SDKUI_Localizator.LastRead }), _jsx("hr", { style: { flex: 1, border: 'none', borderTop: `1px solid ${TMColors.primary}`, marginLeft: '15px' } })] })), _jsx(TMConditionalWrapper, { condition: displayMode === "chat", wrapper: children => _jsx("div", { style: {
571
+ display: "flex",
572
+ flexDirection: "row",
573
+ alignItems: "flex-start",
574
+ padding: "2px",
575
+ borderRadius: "12px",
576
+ cursor: "default",
577
+ justifyContent: isOwnComment ? "flex-end" : "flex-start",
578
+ }, children: _jsx("div", { style: {
579
+ display: "flex",
580
+ flexDirection: "column",
581
+ alignItems: isOwnComment ? "flex-end" : "flex-start",
582
+ textAlign: "left",
583
+ maxWidth: '90%',
584
+ borderRadius: '8px',
585
+ }, children: children }) }), children: _jsxs("div", { id: `${id}-blogpost-${blogPost.id}`, onMouseEnter: () => handleMouseEnter(blogPost), onMouseLeave: handleMouseLeave, onClick: () => handleClick(blogPost), onDoubleClick: () => handleDoubleClick(blogPost), style: {
586
+ display: "flex",
587
+ flexDirection: "column",
588
+ width: "100%",
589
+ position: "relative",
590
+ backgroundColor: isSelected ? "#135596"
591
+ : displayMode === 'stacked' ? "#f9f9f9" : isOwnComment ? '#D9EFE0' : '#F3E6E6',
592
+ transition: "all 0.2s ease",
593
+ color: isSys ? "#ff0000" : isSelected ? "#ffffff" : TMColors.primary,
594
+ borderRadius: "5px",
595
+ padding: isHomeBlogPost ? '10px' : `10px ${isOwnComment ? '50px' : '10px'} 10px 10px`,
596
+ borderLeft: "5px solid transparent",
597
+ textDecoration: blogPost.isDel ? 'line-through' : 'none',
598
+ boxShadow: isFocused ? "0 4px 12px rgba(19, 85, 150, 0.6)" : "none",
599
+ cursor: 'pointer',
600
+ }, children: [_jsx(BlogPostTitle, { displayMode: displayMode, layoutMode: layoutMode, blogPost: blogPost, isSelected: isSelected, isOwnComment: isOwnComment, searchText: searchText, isSys: isSys, isHomeBlogPost: isHomeBlogPost, showId: localShowId, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), isNew && _jsx(NewBadge, { layoutMode: layoutMode }), _jsx("div", { style: { fontSize: '1rem', color: "#000", marginTop: "10px", overflow: "hidden" }, children: _jsx(TMHtmlContentDisplay, { markup: blogPost.description ?? '-', searchText: searchText, isSelected: isSelected }) }), showExtendedAttachments && blogPost.attachments && blogPost.attachments.length > 0 && (_jsx(TMBlogAttachments, { contextMenuParams: contextMenuParams, attachments: blogPost.attachments, layoutMode: layoutMode, isSelected: isSelected, searchText: searchText, dcmtTypeDescriptors: dcmtTypeDescriptors, treeFs: treeFs, draftLatestInfoMap: draftLatestInfoMap, archivedDocumentMap: archivedDocumentMap, context: context, handleAttachmentFocus: handleFocusedAttachment, openDcmtForm: openDcmtForm }))] }, `${id}-blogpost-${blogPost.id}`) })] }, "blog-post-wrapper-" + id + "-" + blogPost.id);
601
+ }), _jsx("div", { ref: bottomRef }), anchorEl && _jsx("div", { style: { position: 'fixed', zIndex: 9999 }, children: _jsx(ContextMenu, { dataSource: menuItems, target: anchorEl, onHiding: closeContextMenu }) })] }), (dcmtForm.dcmt && dcmtForm.dcmt.TID && dcmtForm.dcmt.DID) && _jsx(TMDcmtForm, { TID: Number(dcmtForm.dcmt.TID), DID: Number(dcmtForm.dcmt.DID), layoutMode: LayoutModes.Update, onClose: closeDcmtForm, isClosable: true, titleModal: SDKUI_Localizator.Attachment + ": " + dcmtForm.dcmt.fileName, isModal: true, widthModal: "95%", heightModal: "95%", allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), (showFloatingCommentButton && showCommentFormCallback && !(context?.engine === 'WorkingGroupEngine' && context?.object?.customData1 === 1)) && _jsx("button", { style: {
602
+ position: 'absolute',
603
+ bottom: '18px',
604
+ right: '20px',
605
+ width: '40px',
606
+ height: '40px',
607
+ borderRadius: "50%",
608
+ backgroundColor: "#C2388B",
609
+ color: '#fff',
610
+ border: 'none',
611
+ cursor: 'pointer',
612
+ boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
613
+ zIndex: 1000,
614
+ transition: 'background-color 0.3s ease, transform 0.2s ease',
615
+ display: 'flex',
616
+ justifyContent: 'center',
617
+ alignItems: 'center',
618
+ }, onMouseEnter: (e) => {
619
+ e.currentTarget.style.backgroundColor = '#D94A9F';
620
+ e.currentTarget.style.transform = 'scale(1.1)';
621
+ e.currentTarget.style.boxShadow = '0 4px 12px rgba(37, 89, 165, 0.6)';
622
+ }, onMouseLeave: (e) => {
623
+ e.currentTarget.style.backgroundColor = "#C2388B";
624
+ e.currentTarget.style.transform = 'scale(1)';
625
+ e.currentTarget.style.boxShadow = '0 2px 6px rgba(0,0,0,0.2)';
626
+ }, onClick: () => { showCommentFormCallback(undefined); }, children: _jsx(TMTooltip, { content: SDKUI_Localizator.AddNewComment, children: _jsx("i", { className: "dx-icon-chat", style: { fontSize: '25px' } }) }) })] })] }) }) });
627
+ };
628
+ export default TMBlogsPost;