@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,655 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useCallback, useMemo, useEffect } from 'react';
3
+ import { SDKUI_Localizator } from '../helper/SDKUI_Localizator';
4
+ import { IconPair, IconUnpair } from '../helper/TMIcons';
5
+ import { hasDetailRelations, hasMasterRelations } from '../helper/dcmtsHelper';
6
+ import { SDK_Globals, SearchEngine, RelationCacheService, RelationTypes, DcmtTypeListCacheService, SystemMIDsAsNumber, AccessLevelsEx } from '@topconsultnpm/sdk-ts';
7
+ import { TMSpinner, TMExceptionBoxManager, ShowAlert } from '../components';
8
+ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, currentSearchResults }) => {
9
+ const [relatedDcmts, setRelatedDcmts] = useState(undefined);
10
+ const [showRelatedDcmtsChooser, setShowRelatedDcmtsChooser] = useState(false);
11
+ const [archiveType, setArchiveType] = useState(undefined);
12
+ const [isOpenDetails, setIsOpenDetails] = useState(false);
13
+ const [isOpenMaster, setIsOpenMaster] = useState(false);
14
+ const [isOpenArchiveRelationForm, setIsOpenArchiveRelationForm] = useState(false);
15
+ const [archiveRelatedDcmtFormTID, setArchiveRelatedDcmtFormTID] = useState(undefined);
16
+ const [archiveRelatedDcmtFormMids, setArchiveRelatedDcmtFormMids] = useState([]);
17
+ const [relatedDcmtsChooserDataSource, setRelatedDcmtsChooserDataSource] = useState(undefined);
18
+ const [showPairDcmtsModal, setShowPairDcmtsModal] = useState(false);
19
+ const [isPairingManyToMany, setIsPairingManyToMany] = useState(false);
20
+ const [pairedSearchResults, setPairedSearchResults] = useState([]);
21
+ const [manyToManyRelations, setManyToManyRelations] = useState(undefined);
22
+ const [selectedManyToManyRelation, setSelectedManyToManyRelation] = useState(undefined);
23
+ const [showManyToManyChooser, setShowManyToManyChooser] = useState(false);
24
+ const [manyToManyChooserDataSource, setManyToManyChooserDataSource] = useState(undefined);
25
+ const [showPairSearchModal, setShowPairSearchModal] = useState(false);
26
+ const [pairSearchModalTargetTID, setPairSearchModalTargetTID] = useState(undefined);
27
+ const [pairSearchModalParentTID, setPairSearchModalParentTID] = useState(undefined);
28
+ const [pairSearchModalParentDID, setPairSearchModalParentDID] = useState(undefined);
29
+ const [pairSearchModalRelation, setPairSearchModalRelation] = useState(undefined);
30
+ const [pairSearchModalInputMids, setPairSearchModalInputMids] = useState(undefined);
31
+ const [currentTIDHasDetailRelations, setCurrentTIDHasDetailRelations] = useState();
32
+ const [currentTIDHasMasterRelations, setCurrentTIDHasMasterRelations] = useState();
33
+ const [canArchiveMasterRelation, setCanArchiveMasterRelation] = useState(false);
34
+ const [canArchiveDetailRelation, setCanArchiveDetailRelation] = useState(false);
35
+ const [hasManyToManyRelation, setHasManyToManyRelation] = useState(false);
36
+ useEffect(() => {
37
+ const updateRelationStates = async () => {
38
+ if (selectedSearchResult?.fromTID) {
39
+ const [hasDetail, hasMaster] = await Promise.all([
40
+ hasDetailRelations(selectedSearchResult.fromTID),
41
+ hasMasterRelations(selectedSearchResult.fromTID)
42
+ ]);
43
+ setCurrentTIDHasDetailRelations(hasDetail);
44
+ setCurrentTIDHasMasterRelations(hasMaster);
45
+ }
46
+ else {
47
+ setCurrentTIDHasDetailRelations(undefined);
48
+ setCurrentTIDHasMasterRelations(undefined);
49
+ }
50
+ };
51
+ updateRelationStates();
52
+ }, [selectedSearchResult?.fromTID]);
53
+ const openPairSearchModal = useCallback((relation, targetTID, qd) => {
54
+ if (!targetTID)
55
+ return;
56
+ const inputMidsFromQd = [];
57
+ if (qd?.where) {
58
+ for (const whereItem of qd.where) {
59
+ if (whereItem.mid && whereItem.value1) {
60
+ inputMidsFromQd.push({
61
+ mid: whereItem.mid,
62
+ value: whereItem.value1
63
+ });
64
+ }
65
+ }
66
+ }
67
+ setIsPairingManyToMany(true);
68
+ setPairSearchModalTargetTID(targetTID);
69
+ setPairSearchModalParentTID(Number(focusedItem.TID));
70
+ setPairSearchModalParentDID(Number(focusedItem.DID));
71
+ setPairSearchModalRelation(relation);
72
+ setPairSearchModalInputMids(inputMidsFromQd.length > 0 ? inputMidsFromQd : undefined);
73
+ setShowPairSearchModal(true);
74
+ }, [focusedItem]);
75
+ const createPairFloatingActionConfig = useCallback((relation, parentTID, parentDID, isPairing, onCloseModal) => ({
76
+ isVisible: true,
77
+ type: 'multi',
78
+ onClick: async (selected) => {
79
+ if (!relation || !parentTID || !parentDID)
80
+ return;
81
+ const parentIsMaster = parentTID === relation.masterTID;
82
+ const parentIsDetail = parentTID === relation.detailTID;
83
+ if (!parentIsMaster && !parentIsDetail)
84
+ return;
85
+ const tms = SDK_Globals.tmSession;
86
+ if (!tms)
87
+ return;
88
+ const se = new SearchEngine(tms);
89
+ try {
90
+ TMSpinner.show({ description: isPairing ? 'Abbinamento in corso...' : 'Disabbinamento in corso...' });
91
+ for (const doc of selected) {
92
+ if (!doc?.TID || !doc?.DID)
93
+ continue;
94
+ let masterTID;
95
+ let masterDID;
96
+ let detailTID;
97
+ let detailDID;
98
+ if (parentIsMaster) {
99
+ masterTID = parentTID;
100
+ masterDID = parentDID;
101
+ detailTID = doc.TID;
102
+ detailDID = doc.DID;
103
+ }
104
+ else {
105
+ masterTID = doc.TID;
106
+ masterDID = doc.DID;
107
+ detailTID = parentTID;
108
+ detailDID = parentDID;
109
+ }
110
+ if (isPairing) {
111
+ await se.ManyToManyRelateAsync(masterTID, masterDID, detailTID, detailDID);
112
+ }
113
+ else {
114
+ await se.ManyToManyUnrelateAsync(masterTID, masterDID, detailTID, detailDID);
115
+ }
116
+ }
117
+ onCloseModal();
118
+ }
119
+ catch (error) {
120
+ console.error('Error in onClick:', error);
121
+ TMExceptionBoxManager.show({ exception: error });
122
+ }
123
+ finally {
124
+ TMSpinner.hide();
125
+ }
126
+ },
127
+ iconElement: isPairing ? _jsx(IconPair, { fontSize: 26 }) : _jsx(IconUnpair, { fontSize: 26 }),
128
+ tooltip: isPairing ? 'Abbina' : 'Disabbina',
129
+ }), []);
130
+ const pairFloatingActionConfig = useMemo(() => createPairFloatingActionConfig(selectedManyToManyRelation, focusedItem?.TID ? Number(focusedItem.TID) : undefined, focusedItem?.DID ? Number(focusedItem.DID) : undefined, isPairingManyToMany, () => setShowPairDcmtsModal(false)), [createPairFloatingActionConfig, selectedManyToManyRelation, focusedItem, isPairingManyToMany]);
131
+ const pairSearchModalFloatingActionConfig = useMemo(() => createPairFloatingActionConfig(pairSearchModalRelation, pairSearchModalParentTID, pairSearchModalParentDID, true, () => setShowPairSearchModal(false)), [createPairFloatingActionConfig, pairSearchModalRelation, pairSearchModalParentTID, pairSearchModalParentDID]);
132
+ const getAllRelationsAsync = async () => {
133
+ const tmSession = SDK_Globals.tmSession;
134
+ try {
135
+ TMSpinner.show({ description: SDKUI_Localizator.Loading });
136
+ let relations = await RelationCacheService.GetAllAsync(tmSession);
137
+ if (!relations)
138
+ throw new Error("Impossibile caricare le relazioni.");
139
+ return relations;
140
+ }
141
+ catch (error) {
142
+ TMExceptionBoxManager.show({ exception: error });
143
+ return undefined;
144
+ }
145
+ finally {
146
+ TMSpinner.hide();
147
+ }
148
+ };
149
+ const checkRelatedDcmtsArchiveCapability = useCallback(async () => {
150
+ const tid = selectedSearchResult?.fromTID;
151
+ if (!tid) {
152
+ setCanArchiveMasterRelation(false);
153
+ setCanArchiveDetailRelation(false);
154
+ return;
155
+ }
156
+ try {
157
+ const relations = await getAllRelationsAsync();
158
+ if (!relations) {
159
+ setCanArchiveMasterRelation(false);
160
+ setCanArchiveDetailRelation(false);
161
+ return;
162
+ }
163
+ const detailRelations = relations.filter(r => r.detailTID === tid);
164
+ const hasMasterWithAssociations = detailRelations.some(rel => rel.associations && rel.associations.length > 0);
165
+ setCanArchiveMasterRelation(hasMasterWithAssociations);
166
+ const masterRelations = relations.filter(r => r.masterTID === tid);
167
+ const hasDetailWithAssociations = masterRelations.some(rel => rel.associations && rel.associations.length > 0);
168
+ setCanArchiveDetailRelation(hasDetailWithAssociations);
169
+ }
170
+ catch (error) {
171
+ console.error("Error checking archive capability:", error);
172
+ setCanArchiveMasterRelation(false);
173
+ setCanArchiveDetailRelation(false);
174
+ }
175
+ }, [selectedSearchResult?.fromTID]);
176
+ const checkManyToManyCapability = useCallback(async () => {
177
+ const tid = selectedSearchResult?.fromTID;
178
+ if (!tid) {
179
+ setHasManyToManyRelation(false);
180
+ return;
181
+ }
182
+ try {
183
+ const relations = await getAllRelationsAsync();
184
+ if (!relations) {
185
+ setHasManyToManyRelation(false);
186
+ return;
187
+ }
188
+ const manyToManyRels = relations.filter(r => r.relationType === RelationTypes.ManyToMany &&
189
+ (r.masterTID === tid || r.detailTID === tid));
190
+ setHasManyToManyRelation(manyToManyRels.length > 0);
191
+ }
192
+ catch (error) {
193
+ console.error("Error checking many-to-many capability:", error);
194
+ setHasManyToManyRelation(false);
195
+ }
196
+ }, [selectedSearchResult?.fromTID]);
197
+ const getFocusedItem = useCallback(() => {
198
+ if (!focusedItem || currentSearchResults.length !== 1)
199
+ return undefined;
200
+ return { mdList: currentSearchResults[0].dtdResult?.rows?.[focusedItem?.rowIndex ?? 0], mids: currentSearchResults[0].selectMIDs ?? [] };
201
+ }, [focusedItem, currentSearchResults]);
202
+ const fetchAssociatedValues = useCallback((mid) => {
203
+ let mdList = getFocusedItem();
204
+ if (!mdList)
205
+ return;
206
+ let index = mdList.mids.findIndex(m => m == mid);
207
+ if (index === -1)
208
+ return;
209
+ return mdList.mdList?.[index];
210
+ }, [getFocusedItem]);
211
+ const filterRelationsByType = (relations, tid, type) => {
212
+ return type === 'detail'
213
+ ? relations.filter(r => r.masterTID == tid)
214
+ : relations.filter(r => r.detailTID == tid);
215
+ };
216
+ const filterRelationsWithAssociations = (relations) => {
217
+ return relations.filter(rel => rel.associations && rel.associations.length > 0);
218
+ };
219
+ const getRelatedDcmt = async (relation, type) => {
220
+ return await DcmtTypeListCacheService.GetAsync(type === 'detail' ? relation.detailTID : relation.masterTID);
221
+ };
222
+ const showNoRelationsAlert = (type) => {
223
+ ShowAlert({
224
+ message: type === 'detail'
225
+ ? "Nessun documento di dettaglio trovato per l'archiviazione."
226
+ : "Nessun documento di master trovato per l'archiviazione.",
227
+ mode: 'info',
228
+ title: type === 'detail' ? SDKUI_Localizator.DcmtsDetail : SDKUI_Localizator.DcmtsMaster,
229
+ duration: 5000
230
+ });
231
+ };
232
+ const showNoAssociationsAlert = (type) => {
233
+ ShowAlert({
234
+ message: type === 'detail'
235
+ ? "Nessuna associazione trovata per i documenti di dettaglio."
236
+ : "Nessuna associazione trovata per i documenti di master.",
237
+ mode: 'info',
238
+ title: type === 'detail' ? SDKUI_Localizator.DcmtsDetail : SDKUI_Localizator.DcmtsMaster,
239
+ duration: 5000
240
+ });
241
+ };
242
+ const mapAssociationsToMids = useCallback((relation, type) => {
243
+ return relation.associations?.map(assoc => ({
244
+ mid: type === 'detail' ? (assoc.item2 ?? 0) : (assoc.item1 ?? 0),
245
+ value: fetchAssociatedValues(type === 'detail' ? (assoc.item1 ?? 0) : (assoc.item2 ?? 0)) ?? ''
246
+ })) ?? [];
247
+ }, [fetchAssociatedValues]);
248
+ const archiveRelatedDcmtHandler = useCallback(async (relation, type) => {
249
+ const targetTID = type === 'detail' ? relation.detailTID : relation.masterTID;
250
+ if (!targetTID)
251
+ return;
252
+ try {
253
+ const dtd = await DcmtTypeListCacheService.GetWithNotGrantedAsync(targetTID, undefined);
254
+ if (dtd?.perm?.canArchive !== AccessLevelsEx.Yes && dtd?.perm?.canArchive !== AccessLevelsEx.Mixed) {
255
+ ShowAlert({
256
+ message: type === 'detail'
257
+ ? "Non hai i permessi per archiviare documenti di dettaglio di questo tipo."
258
+ : "Non hai i permessi per archiviare documenti master di questo tipo.",
259
+ mode: 'warning',
260
+ title: type === 'detail' ? SDKUI_Localizator.DcmtsDetail : SDKUI_Localizator.DcmtsMaster,
261
+ duration: 5000
262
+ });
263
+ return;
264
+ }
265
+ const mids = mapAssociationsToMids(relation, type);
266
+ setArchiveType(type);
267
+ setArchiveRelatedDcmtFormTID(targetTID);
268
+ setArchiveRelatedDcmtFormMids(mids);
269
+ setIsOpenArchiveRelationForm(true);
270
+ }
271
+ catch (error) {
272
+ console.error("Error checking archive permissions:", error);
273
+ TMExceptionBoxManager.show({ exception: error });
274
+ }
275
+ }, [mapAssociationsToMids]);
276
+ const archiveRelatedDocuments = useCallback(async (tid, type) => {
277
+ try {
278
+ TMSpinner.show({ description: SDKUI_Localizator.Loading });
279
+ const relations = await getAllRelationsAsync();
280
+ if (!relations || !tid)
281
+ return;
282
+ const filteredRelations = filterRelationsByType(relations, tid, type);
283
+ if (!filteredRelations || filteredRelations.length === 0) {
284
+ showNoRelationsAlert(type);
285
+ return;
286
+ }
287
+ const withAssociations = filterRelationsWithAssociations(filteredRelations);
288
+ if (withAssociations.length === 0) {
289
+ showNoAssociationsAlert(type);
290
+ return;
291
+ }
292
+ setRelatedDcmts(withAssociations);
293
+ if (withAssociations.length > 1) {
294
+ const dataSourcePromises = withAssociations.map(async (rel) => {
295
+ const relatedDcmt = await getRelatedDcmt(rel, type);
296
+ return { id: rel?.id, name: relatedDcmt?.name };
297
+ });
298
+ const dataSource = await Promise.all(dataSourcePromises);
299
+ setRelatedDcmtsChooserDataSource(dataSource);
300
+ setArchiveType(type);
301
+ setShowRelatedDcmtsChooser(true);
302
+ }
303
+ else {
304
+ await archiveRelatedDcmtHandler(withAssociations[0], type);
305
+ }
306
+ }
307
+ catch (error) {
308
+ TMExceptionBoxManager.show({ exception: error });
309
+ }
310
+ finally {
311
+ TMSpinner.hide();
312
+ }
313
+ }, [archiveRelatedDcmtHandler]);
314
+ const archiveDetailDocuments = useCallback(async (tid) => {
315
+ await archiveRelatedDocuments(tid, 'detail');
316
+ }, [archiveRelatedDocuments]);
317
+ const archiveMasterDocuments = useCallback(async (tid) => {
318
+ await archiveRelatedDocuments(tid, 'master');
319
+ }, [archiveRelatedDocuments]);
320
+ const getPairedDocuments = async (relation, currentTID, currentDID, searchEngine) => {
321
+ if (!relation || !currentDID)
322
+ return null;
323
+ try {
324
+ const isMaster = currentTID === relation.masterTID;
325
+ const pairedResults = isMaster
326
+ ? await searchEngine.GetAllDetailDcmtsAsync(currentTID, currentDID)
327
+ : await searchEngine.GetAllMasterDcmtsAsync(currentTID, currentDID);
328
+ if (!pairedResults || pairedResults.length === 0)
329
+ return null;
330
+ const relationResult = pairedResults.find(r => r.relationID === relation.id);
331
+ if (!relationResult?.dtdResult)
332
+ return null;
333
+ return relationResult;
334
+ }
335
+ catch (error) {
336
+ console.error('getPairedDocuments - Error:', error);
337
+ return null;
338
+ }
339
+ };
340
+ const extractPairedDIDs = (relationResult) => {
341
+ const pairedDIDs = [];
342
+ const rows = relationResult.dtdResult?.rows ?? [];
343
+ const columns = relationResult.dtdResult?.columns ?? [];
344
+ const didColumnIndex = columns.findIndex(col => {
345
+ const caption = col.caption?.toUpperCase();
346
+ const mid = col.extendedProperties?.["MID"];
347
+ const midNum = typeof mid === 'string' ? Number.parseInt(mid, 10) : mid;
348
+ return caption === 'DID' || midNum === 5;
349
+ });
350
+ if (didColumnIndex === -1)
351
+ return [];
352
+ for (const row of rows) {
353
+ const did = row[didColumnIndex];
354
+ if (did) {
355
+ const didNumber = typeof did === 'string' ? Number.parseInt(did, 10) : did;
356
+ if (!Number.isNaN(didNumber)) {
357
+ pairedDIDs.push(didNumber);
358
+ }
359
+ }
360
+ }
361
+ return pairedDIDs;
362
+ };
363
+ const executeManyToManyPairing = useCallback(async (relation, isPairing) => {
364
+ const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
365
+ if (!focusedItem?.TID || !focusedItem?.DID) {
366
+ ShowAlert({
367
+ message: isPairing
368
+ ? "Nessun documento selezionato per l'abbinamento molti a molti."
369
+ : "Nessun documento selezionato per il disabbinamento molti a molti.",
370
+ mode: 'warning',
371
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
372
+ duration: 5000
373
+ });
374
+ return;
375
+ }
376
+ setSelectedManyToManyRelation(relation);
377
+ let qd;
378
+ if (relation.detailTID === selectedSearchResult?.fromTID) {
379
+ qd = relation.retrieveMastersQuery;
380
+ }
381
+ if (relation.masterTID === selectedSearchResult?.fromTID) {
382
+ qd = relation.retrieveDetailsQuery;
383
+ }
384
+ if (!qd) {
385
+ ShowAlert({
386
+ message: isPairing
387
+ ? "Nessuna query di recupero associata alla relazione di abbinamento molti a molti."
388
+ : "Nessuna query di recupero associata alla relazione di disabbinamento molti a molti.",
389
+ mode: 'warning',
390
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
391
+ duration: 5000
392
+ });
393
+ return;
394
+ }
395
+ qd = structuredClone(qd);
396
+ if (qd.select) {
397
+ for (const s of qd.select) {
398
+ s.visibility = 1;
399
+ }
400
+ }
401
+ const qdRetrieveParamsValue = {
402
+ from: { tid: focusedItem.TID },
403
+ select: [],
404
+ where: [
405
+ {
406
+ tid: focusedItem.TID,
407
+ mid: 5,
408
+ leftBrackets: "(",
409
+ rightBrackets: ")",
410
+ operator: 0,
411
+ value1: focusedItem.DID.toString()
412
+ }
413
+ ]
414
+ };
415
+ let notMappedMIDs = false;
416
+ if (qd.params && qd.params.length > 0) {
417
+ for (const param of qd.params) {
418
+ if (!param.mappedMID)
419
+ continue;
420
+ qdRetrieveParamsValue.select.push({
421
+ tid: param.mappedTID ?? focusedItem.TID,
422
+ mid: param.mappedMID,
423
+ visibility: 1
424
+ });
425
+ const currentRowValue = currentSearchResults[0]?.dtdResult?.rows?.[focusedItem.rowIndex];
426
+ const midIndex = currentSearchResults[0]?.selectMIDs?.indexOf(param.mappedMID);
427
+ if (currentRowValue && midIndex !== undefined && midIndex >= 0) {
428
+ const value = currentRowValue[midIndex];
429
+ param.value = value ?? '';
430
+ if (param.value === null || param.value === '')
431
+ notMappedMIDs = true;
432
+ }
433
+ else {
434
+ notMappedMIDs = true;
435
+ }
436
+ }
437
+ if (notMappedMIDs && qdRetrieveParamsValue.select && qdRetrieveParamsValue.select.length > 0) {
438
+ const paramMetadata = await searchEngine.SearchByIDAsync(qdRetrieveParamsValue);
439
+ if (paramMetadata?.dtdResult?.rows?.[0]) {
440
+ for (const param of qd.params) {
441
+ if (!param.mappedMID)
442
+ continue;
443
+ const colIndex = paramMetadata.selectMIDs?.indexOf(param.mappedMID);
444
+ if (colIndex !== undefined && colIndex >= 0) {
445
+ const value = paramMetadata.dtdResult.rows[0][colIndex];
446
+ param.value = value ?? '';
447
+ }
448
+ }
449
+ }
450
+ }
451
+ if (qd.where && qd.where.length > 0) {
452
+ for (const whereItem of qd.where) {
453
+ for (const param of qd.params) {
454
+ if (!param.name)
455
+ continue;
456
+ param.value ??= '';
457
+ const paramValue = param.value.toString();
458
+ if (whereItem.value1) {
459
+ whereItem.value1 = whereItem.value1.replace(param.name, paramValue);
460
+ }
461
+ if (whereItem.value2) {
462
+ whereItem.value2 = whereItem.value2.replace(param.name, paramValue);
463
+ }
464
+ }
465
+ }
466
+ }
467
+ }
468
+ const targetTID = relation.detailTID === selectedSearchResult?.fromTID
469
+ ? relation.masterTID
470
+ : relation.detailTID;
471
+ const systemSelects = [
472
+ { tid: targetTID, mid: SystemMIDsAsNumber.DID, visibility: 0 },
473
+ { tid: targetTID, mid: SystemMIDsAsNumber.TID, visibility: 0 },
474
+ { tid: targetTID, mid: SystemMIDsAsNumber.OwnerID, visibility: 0 },
475
+ { tid: targetTID, mid: SystemMIDsAsNumber.UpdaterID, visibility: 0 },
476
+ { tid: targetTID, mid: SystemMIDsAsNumber.FileExt, visibility: 0 },
477
+ { tid: targetTID, mid: SystemMIDsAsNumber.CreationTime, visibility: 0 },
478
+ { tid: targetTID, mid: SystemMIDsAsNumber.LastUpdateTime, visibility: 0 },
479
+ { tid: targetTID, mid: SystemMIDsAsNumber.FileCount, visibility: 0 },
480
+ { tid: targetTID, mid: SystemMIDsAsNumber.FileSize, visibility: 0 },
481
+ { tid: targetTID, mid: SystemMIDsAsNumber.PageCount, visibility: 0 },
482
+ ];
483
+ if (qd.select) {
484
+ qd.select = [...systemSelects, ...qd.select];
485
+ }
486
+ else {
487
+ qd.select = systemSelects;
488
+ }
489
+ const pairedDocumentsResult = await getPairedDocuments(relation, focusedItem.TID, focusedItem.DID, searchEngine);
490
+ if (!isPairing) {
491
+ if (!pairedDocumentsResult?.dtdResult?.rows || pairedDocumentsResult.dtdResult.rows.length === 0) {
492
+ ShowAlert({
493
+ message: "Nessun documento abbinato trovato.",
494
+ mode: 'warning',
495
+ title: 'Disabbina documenti molti a molti',
496
+ duration: 5000
497
+ });
498
+ return;
499
+ }
500
+ const pairedSq = {
501
+ fromTID: targetTID,
502
+ fromName: await DcmtTypeListCacheService.GetAsync(targetTID).then(d => d?.name ?? ''),
503
+ dtdResult: pairedDocumentsResult.dtdResult,
504
+ selectMIDs: pairedDocumentsResult.selectMIDs,
505
+ relationID: relation.id,
506
+ dcmtsReturned: pairedDocumentsResult.dtdResult.rows.length,
507
+ dcmtsFound: pairedDocumentsResult.dtdResult.rows.length
508
+ };
509
+ setPairedSearchResults([pairedSq]);
510
+ setIsPairingManyToMany(isPairing);
511
+ setShowPairDcmtsModal(true);
512
+ return;
513
+ }
514
+ const sq = await searchEngine.SearchByIDAsync(qd);
515
+ if (!sq?.dtdResult?.rows || sq.dtdResult.rows.length === 0) {
516
+ ShowAlert({
517
+ message: "Nessun documento trovato.",
518
+ mode: 'warning',
519
+ title: 'Abbina documenti molti a molti',
520
+ duration: 5000
521
+ });
522
+ openPairSearchModal(relation, targetTID, qd);
523
+ return;
524
+ }
525
+ const pairedDIDs = pairedDocumentsResult ? extractPairedDIDs(pairedDocumentsResult) : [];
526
+ const filteredRows = sq.dtdResult.rows.filter(row => {
527
+ const did = row[0];
528
+ const didNum = typeof did === 'string' ? Number.parseInt(did, 10) : did;
529
+ return !pairedDIDs.includes(didNum);
530
+ });
531
+ const filteredSq = {
532
+ ...sq,
533
+ dtdResult: {
534
+ ...sq.dtdResult,
535
+ rows: filteredRows
536
+ },
537
+ dcmtsReturned: filteredRows.length,
538
+ dcmtsFound: filteredRows.length
539
+ };
540
+ if (filteredRows.length === 0) {
541
+ ShowAlert({
542
+ message: "Nessun documento da abbinare. Tutti i documenti risultanti sono già abbinati.",
543
+ mode: 'warning',
544
+ title: 'Abbina documenti molti a molti',
545
+ duration: 5000
546
+ });
547
+ openPairSearchModal(relation, targetTID, qd);
548
+ return;
549
+ }
550
+ setPairedSearchResults([filteredSq]);
551
+ setIsPairingManyToMany(isPairing);
552
+ setShowPairDcmtsModal(true);
553
+ }, [focusedItem, selectedSearchResult, currentSearchResults, openPairSearchModal]);
554
+ const pairManyToMany = useCallback(async (isPairing) => {
555
+ try {
556
+ TMSpinner.show({ description: SDKUI_Localizator.Loading });
557
+ const relations = await getAllRelationsAsync();
558
+ if (!relations)
559
+ return;
560
+ if (!relations.some(r => r.relationType === RelationTypes.ManyToMany)) {
561
+ ShowAlert({
562
+ message: isPairing
563
+ ? "Nessuna relazione di abbinamento molti a molti definita nel sistema."
564
+ : "Nessuna relazione di disabbinamento molti a molti definita nel sistema.",
565
+ mode: 'warning',
566
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
567
+ duration: 5000
568
+ });
569
+ return;
570
+ }
571
+ const manyToManyRels = relations.filter(r => r.relationType === RelationTypes.ManyToMany);
572
+ if (!manyToManyRels.some(r => r.masterTID === selectedSearchResult?.fromTID || r.detailTID === selectedSearchResult?.fromTID)) {
573
+ ShowAlert({
574
+ message: isPairing
575
+ ? "Nessuna relazione di abbinamento molti a molti definita per il tipo di documento selezionato."
576
+ : "Nessuna relazione di disabbinamento molti a molti definita per il tipo di documento selezionato.",
577
+ mode: 'warning',
578
+ title: isPairing ? 'Abbina documenti molti a molti' : 'Disabbina documenti molti a molti',
579
+ duration: 5000
580
+ });
581
+ return;
582
+ }
583
+ const relsManyToMany = manyToManyRels.filter(r => r.masterTID === selectedSearchResult?.fromTID || r.detailTID === selectedSearchResult?.fromTID);
584
+ setManyToManyRelations(relsManyToMany);
585
+ if (relsManyToMany.length > 1) {
586
+ const dataSourcePromises = relsManyToMany.map(async (rel) => {
587
+ const targetTID = rel.masterTID === selectedSearchResult?.fromTID ? rel.detailTID : rel.masterTID;
588
+ const relatedDcmt = await DcmtTypeListCacheService.GetAsync(targetTID);
589
+ return { id: rel?.id, name: relatedDcmt?.name };
590
+ });
591
+ const dataSource = await Promise.all(dataSourcePromises);
592
+ setManyToManyChooserDataSource(dataSource);
593
+ setIsPairingManyToMany(isPairing);
594
+ setShowManyToManyChooser(true);
595
+ }
596
+ else {
597
+ await executeManyToManyPairing(relsManyToMany[0], isPairing);
598
+ }
599
+ }
600
+ catch (error) {
601
+ TMExceptionBoxManager.show({ exception: error });
602
+ }
603
+ finally {
604
+ TMSpinner.hide();
605
+ }
606
+ }, [selectedSearchResult, executeManyToManyPairing]);
607
+ return {
608
+ relatedDcmts,
609
+ showRelatedDcmtsChooser,
610
+ archiveType,
611
+ isOpenDetails,
612
+ isOpenMaster,
613
+ isOpenArchiveRelationForm,
614
+ archiveRelatedDcmtFormTID,
615
+ archiveRelatedDcmtFormMids,
616
+ relatedDcmtsChooserDataSource,
617
+ showPairDcmtsModal,
618
+ isPairingManyToMany,
619
+ pairedSearchResults,
620
+ manyToManyRelations,
621
+ selectedManyToManyRelation,
622
+ showManyToManyChooser,
623
+ manyToManyChooserDataSource,
624
+ showPairSearchModal,
625
+ pairSearchModalTargetTID,
626
+ pairSearchModalParentTID,
627
+ pairSearchModalParentDID,
628
+ pairSearchModalRelation,
629
+ pairSearchModalInputMids,
630
+ currentTIDHasDetailRelations,
631
+ currentTIDHasMasterRelations,
632
+ canArchiveMasterRelation,
633
+ canArchiveDetailRelation,
634
+ hasManyToManyRelation,
635
+ setIsOpenDetails,
636
+ setIsOpenMaster,
637
+ setShowRelatedDcmtsChooser,
638
+ setShowManyToManyChooser,
639
+ setShowPairDcmtsModal,
640
+ setShowPairSearchModal,
641
+ setIsOpenArchiveRelationForm,
642
+ setArchiveType,
643
+ setArchiveRelatedDcmtFormTID,
644
+ setArchiveRelatedDcmtFormMids,
645
+ pairFloatingActionConfig,
646
+ pairSearchModalFloatingActionConfig,
647
+ archiveMasterDocuments,
648
+ archiveDetailDocuments,
649
+ pairManyToMany,
650
+ checkRelatedDcmtsArchiveCapability,
651
+ checkManyToManyCapability,
652
+ archiveRelatedDcmtHandler,
653
+ executeManyToManyPairing,
654
+ };
655
+ };
package/lib/index.d.ts CHANGED
@@ -8,4 +8,5 @@ export * from './hooks/useQueryParametersDialog';
8
8
  export * from './hooks/useDcmtOperations';
9
9
  export * from './hooks/useResizeObserver';
10
10
  export * from './hooks/useWorkflowApprove';
11
+ export * from './hooks/useRelatedDocuments';
11
12
  export * from './services';
package/lib/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './hooks/useQueryParametersDialog';
8
8
  export * from './hooks/useDcmtOperations';
9
9
  export * from './hooks/useResizeObserver';
10
10
  export * from './hooks/useWorkflowApprove';
11
+ export * from './hooks/useRelatedDocuments';
11
12
  export * from './services';
12
13
  import config from 'devextreme/core/config';
13
14
  // DevExtreme License Key (valid for v25.1 and earlier versions)