@topconsultnpm/sdkui-react 6.20.0-test1 → 6.21.0-dev1.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.
- package/lib/components/NewComponents/ContextMenu/styles.d.ts +3 -1
- package/lib/components/NewComponents/ContextMenu/styles.js +7 -5
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +7 -1
- package/lib/components/base/Styled.d.ts +4 -1
- package/lib/components/base/Styled.js +11 -3
- package/lib/components/base/TMPanel.js +6 -4
- package/lib/components/base/TMPopUp.js +4 -0
- package/lib/components/base/TMTreeView.d.ts +3 -1
- package/lib/components/base/TMTreeView.js +68 -21
- package/lib/components/base/TMWaitPanel.js +6 -5
- package/lib/components/choosers/TMDataListItemChooser.js +1 -1
- package/lib/components/choosers/TMDataListItemEditor.d.ts +11 -0
- package/lib/components/choosers/TMDataListItemEditor.js +130 -0
- package/lib/components/choosers/TMDataListItemFields.d.ts +11 -0
- package/lib/components/choosers/TMDataListItemFields.js +61 -0
- package/lib/components/choosers/TMDataListItemPicker.d.ts +2 -0
- package/lib/components/choosers/TMDataListItemPicker.js +182 -18
- package/lib/components/choosers/TMDynDataListItemChooser.js +11 -6
- package/lib/components/choosers/TMImageIDChooser.d.ts +16 -0
- package/lib/components/choosers/TMImageIDChooser.js +53 -0
- package/lib/components/choosers/TMMetadataChooser.js +1 -1
- package/lib/components/choosers/TMUserChooser.js +1 -1
- package/lib/components/editors/TMDateBox.js +1 -1
- package/lib/components/editors/TMHtmlEditor.js +1 -1
- package/lib/components/editors/TMLocalizedTextBox.d.ts +1 -0
- package/lib/components/editors/TMLocalizedTextBox.js +3 -3
- package/lib/components/editors/TMMetadataValues.js +203 -41
- package/lib/components/editors/TMTextArea.d.ts +1 -0
- package/lib/components/editors/TMTextArea.js +6 -6
- package/lib/components/editors/TMTextBox.js +9 -10
- package/lib/components/features/archive/TMArchive.d.ts +3 -1
- package/lib/components/features/archive/TMArchive.js +31 -44
- package/lib/components/features/blog/TMBlogCommentForm.d.ts +3 -0
- package/lib/components/features/blog/TMBlogCommentForm.js +42 -36
- package/lib/components/features/documents/TMDcmtBlog.d.ts +1 -0
- package/lib/components/features/documents/TMDcmtBlog.js +2 -1
- package/lib/components/features/documents/TMDcmtForm.d.ts +44 -34
- package/lib/components/features/documents/TMDcmtForm.js +365 -563
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +34 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +124 -0
- package/lib/components/features/documents/TMDcmtPreview.js +66 -13
- package/lib/components/features/documents/TMDcmtTasks.d.ts +3 -1
- package/lib/components/features/documents/TMDcmtTasks.js +2 -2
- package/lib/components/features/documents/TMFileUploader.d.ts +5 -0
- package/lib/components/features/documents/TMFileUploader.js +28 -6
- package/lib/components/features/documents/TMMasterDetailDcmts.js +31 -85
- package/lib/components/features/documents/TMRelationViewer.d.ts +7 -1
- package/lib/components/features/documents/TMRelationViewer.js +497 -111
- package/lib/components/features/search/TMSearch.d.ts +2 -2
- package/lib/components/features/search/TMSearch.js +3 -3
- package/lib/components/features/search/TMSearchQueryPanel.js +6 -6
- package/lib/components/features/search/TMSearchResult.d.ts +28 -25
- package/lib/components/features/search/TMSearchResult.js +445 -562
- package/lib/components/features/search/TMSignatureInfoContent.js +10 -6
- package/lib/components/features/search/TMTreeSelector.js +1 -1
- package/lib/components/features/tasks/TMTaskForm.d.ts +3 -1
- package/lib/components/features/tasks/TMTaskForm.js +61 -193
- package/lib/components/features/tasks/TMTaskFormUtils.d.ts +80 -0
- package/lib/components/features/tasks/TMTaskFormUtils.js +559 -0
- package/lib/components/features/tasks/TMTasksUtils.d.ts +3 -1
- package/lib/components/features/tasks/TMTasksUtils.js +46 -16
- package/lib/components/features/tasks/TMTasksUtilsView.d.ts +0 -7
- package/lib/components/features/tasks/TMTasksUtilsView.js +7 -14
- package/lib/components/features/tasks/TMTasksView.js +5 -3
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +20 -3
- package/lib/components/features/workflow/TMWorkflowPopup.js +21 -109
- package/lib/components/features/workflow/diagram/ConnectionComponent.d.ts +1 -0
- package/lib/components/features/workflow/diagram/ConnectionComponent.js +6 -2
- package/lib/components/features/workflow/diagram/DiagramItemForm.d.ts +2 -0
- package/lib/components/features/workflow/diagram/DiagramItemForm.js +32 -25
- package/lib/components/features/workflow/diagram/RecipientList.d.ts +3 -1
- package/lib/components/features/workflow/diagram/RecipientList.js +13 -9
- package/lib/components/features/workflow/diagram/WFDiagram.js +102 -5
- package/lib/components/features/workflow/diagram/workflowHelpers.js +31 -19
- package/lib/components/forms/Login/TMLoginForm.js +1 -1
- package/lib/components/forms/TMSaveForm.js +61 -13
- package/lib/components/grids/TMBlogsPost.js +8 -8
- package/lib/components/grids/TMBlogsPostUtils.js +2 -2
- package/lib/components/grids/TMRecentsManager.js +1 -1
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +3 -2
- package/lib/components/pages/TMPage.js +4 -0
- package/lib/components/query/TMQueryEditor.d.ts +1 -0
- package/lib/components/query/TMQueryEditor.js +3 -3
- package/lib/components/viewers/TMMidViewer.js +2 -1
- package/lib/components/viewers/TMTidViewer.js +7 -3
- package/lib/helper/Enum_Localizator.js +5 -0
- package/lib/helper/GlobalStyles.js +3 -0
- package/lib/helper/SDKUI_Globals.d.ts +12 -0
- package/lib/helper/SDKUI_Globals.js +21 -1
- package/lib/helper/SDKUI_Localizator.d.ts +31 -7
- package/lib/helper/SDKUI_Localizator.js +286 -46
- package/lib/helper/TMIcons.d.ts +2 -1
- package/lib/helper/TMIcons.js +4 -1
- package/lib/helper/TMUtils.d.ts +33 -41
- package/lib/helper/TMUtils.js +157 -170
- package/lib/helper/checkinCheckoutManager.js +6 -2
- package/lib/helper/helpers.d.ts +6 -2
- package/lib/helper/helpers.js +24 -8
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.js +1 -1
- package/lib/hooks/useBetaFeatures.d.ts +1 -0
- package/lib/hooks/useBetaFeatures.js +41 -0
- package/lib/hooks/useCheckInOutOperations.d.ts +7 -6
- package/lib/hooks/useCheckInOutOperations.js +9 -16
- package/lib/hooks/useDataUserIdItem.js +2 -2
- package/lib/hooks/useDcmtOperations.d.ts +3 -2
- package/lib/hooks/useDcmtOperations.js +16 -4
- package/lib/hooks/useDocumentOperations.d.ts +139 -0
- package/lib/hooks/useDocumentOperations.js +1275 -0
- package/lib/hooks/useRelatedDocuments.d.ts +1 -1
- package/lib/hooks/useRelatedDocuments.js +64 -42
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/lib/services/platform_services.js +4 -0
- package/lib/ts/types.d.ts +5 -1
- package/lib/ts/types.js +1 -0
- package/package.json +55 -55
- package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +0 -11
- package/lib/components/features/search/TMSearchResultsMenuItems.js +0 -770
- package/lib/components/features/search/TMSignSettingsForm.d.ts +0 -9
- package/lib/components/features/search/TMSignSettingsForm.js +0 -621
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataFormats, SystemMIDs, MetadataDataDomains, RelationCacheService, RelationTypes } from "@topconsultnpm/sdk-ts";
|
|
4
|
-
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight } from '../../../helper';
|
|
4
|
+
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo } from '../../../helper';
|
|
5
5
|
import { TMColors } from '../../../utils/theme';
|
|
6
6
|
import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
|
|
7
7
|
import TMTreeView from '../../base/TMTreeView';
|
|
@@ -136,7 +136,7 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata) =>
|
|
|
136
136
|
}
|
|
137
137
|
return output;
|
|
138
138
|
};
|
|
139
|
-
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onDocumentDoubleClick, customItemRender, customDocumentStyle, customMainContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems, showMainDocument = true, labelMainContainer, }) => {
|
|
139
|
+
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onDocumentDoubleClick, customItemRender, customDocumentStyle, customMainContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems, showMainDocument = true, labelMainContainer, onNoRelationsFound, }) => {
|
|
140
140
|
// State
|
|
141
141
|
const [dcmtTypes, setDcmtTypes] = useState([]);
|
|
142
142
|
const [treeData, setTreeData] = useState([]);
|
|
@@ -154,8 +154,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
154
154
|
const [expansionAbortController, setExpansionAbortController] = useState(undefined);
|
|
155
155
|
// Ref to track last loaded input to prevent unnecessary reloads
|
|
156
156
|
const lastLoadedInputRef = React.useRef('');
|
|
157
|
+
// State to track loaded input key - triggers re-render for focus selection
|
|
158
|
+
const [loadedInputKey, setLoadedInputKey] = React.useState('');
|
|
157
159
|
// Ref to track if user has manually expanded/collapsed static items
|
|
158
160
|
const userInteractedWithStaticItemsRef = React.useRef(false);
|
|
161
|
+
// Ref to track the last inputKey for which we set the focused item
|
|
162
|
+
const lastFocusedInputRef = React.useRef('');
|
|
159
163
|
/**
|
|
160
164
|
* Generate a stable key from inputDcmts to detect real changes
|
|
161
165
|
*/
|
|
@@ -219,20 +223,27 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
219
223
|
const source = await searchResultToDataSource(searchResult);
|
|
220
224
|
if (source && source.length > 0) {
|
|
221
225
|
const dcmtDetails = [];
|
|
226
|
+
// Check once if this document type can have detail relations
|
|
227
|
+
// (optimization: avoid checking for each document)
|
|
228
|
+
const canHaveDetails = await hasDetailRelations(searchResult.fromTID);
|
|
222
229
|
for (const row of source) {
|
|
223
230
|
const rowGUID = genUniqueId();
|
|
224
231
|
const tid = row?.TID?.value;
|
|
225
232
|
const did = row?.DID?.value;
|
|
233
|
+
const isLogDel = row?.ISLOGDEL?.value;
|
|
226
234
|
dcmtDetails.push({
|
|
227
235
|
tid: tid,
|
|
228
236
|
did: did,
|
|
237
|
+
isLogDel: isLogDel,
|
|
229
238
|
key: `${tid}_${did}_${searchResult.relationID}_${mTID}_${mDID}_${rowGUID}`,
|
|
230
239
|
isDcmt: true,
|
|
231
240
|
isContainer: false,
|
|
232
241
|
isZero: false, // Documents are never zero (they exist)
|
|
242
|
+
isExpandible: canHaveDetails, // Can this doc be expanded?
|
|
233
243
|
values: row,
|
|
234
244
|
searchResult: [searchResult],
|
|
235
|
-
itemsCount
|
|
245
|
+
// Leave items and itemsCount undefined so TMTreeView shows expand arrow based on isExpandible
|
|
246
|
+
// Children will be loaded lazily by calculateItemsForNode when expanded
|
|
236
247
|
expanded: false,
|
|
237
248
|
hidden: false,
|
|
238
249
|
name: row?.SYS_Abstract?.value || row?.SYS_SUBJECT?.value || `Documento ${did}`
|
|
@@ -293,20 +304,30 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
293
304
|
const source = await searchResultToDataSource(searchResult);
|
|
294
305
|
if (source && source.length > 0) {
|
|
295
306
|
const dcmtMasters = [];
|
|
307
|
+
// Check once if this document type can have master relations (for inverted mode)
|
|
308
|
+
// or detail relations (for standard mode when expanding masters)
|
|
309
|
+
// (optimization: avoid checking for each document)
|
|
310
|
+
const canExpand = isForMaster && invertMasterNavigation
|
|
311
|
+
? await hasMasterRelations(searchResult.fromTID)
|
|
312
|
+
: await hasDetailRelations(searchResult.fromTID);
|
|
296
313
|
for (const row of source) {
|
|
297
314
|
const rowGUID = genUniqueId();
|
|
298
315
|
const tid = row?.TID?.value;
|
|
299
316
|
const did = row?.DID?.value;
|
|
317
|
+
const isLogDel = row?.ISLOGDEL?.value;
|
|
300
318
|
dcmtMasters.push({
|
|
301
319
|
tid: tid,
|
|
302
320
|
did: did,
|
|
321
|
+
isLogDel: isLogDel,
|
|
303
322
|
key: `${tid}_${did}_${searchResult.relationID}_${dTID}_${dDID}_${rowGUID}`,
|
|
304
323
|
isDcmt: true,
|
|
305
324
|
isContainer: false,
|
|
306
325
|
isZero: false, // Documents are never zero (they exist)
|
|
326
|
+
isExpandible: canExpand, // Can this doc be expanded?
|
|
307
327
|
values: row,
|
|
308
328
|
searchResult: [searchResult],
|
|
309
|
-
itemsCount
|
|
329
|
+
// Leave items and itemsCount undefined so TMTreeView shows expand arrow based on isExpandible
|
|
330
|
+
// Children will be loaded lazily by calculateItemsForNode when expanded
|
|
310
331
|
expanded: false,
|
|
311
332
|
hidden: false,
|
|
312
333
|
name: row?.SYS_Abstract?.value || row?.SYS_SUBJECT?.value || `Documento ${did}`
|
|
@@ -324,17 +345,133 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
324
345
|
return items;
|
|
325
346
|
}, [allowedTIDs]);
|
|
326
347
|
/**
|
|
327
|
-
* Update hidden property based on showZeroDcmts
|
|
348
|
+
* Update hidden property based on showZeroDcmts
|
|
349
|
+
* Updates items in-place without resetting isLoaded (no API calls needed)
|
|
328
350
|
*/
|
|
329
351
|
const updateHiddenProperty = useCallback((nodes) => {
|
|
352
|
+
// Safety check: if nodes is undefined or not an array, return empty array
|
|
353
|
+
if (!nodes || !Array.isArray(nodes)) {
|
|
354
|
+
return [];
|
|
355
|
+
}
|
|
330
356
|
return nodes.map(node => {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
357
|
+
let shouldHide = false;
|
|
358
|
+
let updatedItems = undefined;
|
|
359
|
+
if (node.items && Array.isArray(node.items)) {
|
|
360
|
+
// Recursively update children first - this creates a new array
|
|
361
|
+
let updatedChildren = updateHiddenProperty(node.items);
|
|
362
|
+
// Remove any existing auto-generated info messages (with key starting with __info__)
|
|
363
|
+
const filteredChildren = updatedChildren.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
364
|
+
// Check if all real children (excluding info messages) are hidden
|
|
365
|
+
const realChildren = filteredChildren.filter(child => !child.isInfoMessage);
|
|
366
|
+
const allChildrenHidden = realChildren.length > 0 && realChildren.every(child => child.hidden);
|
|
367
|
+
// If node is expanded AND showZeroDcmts is false AND all children are hidden, add info message
|
|
368
|
+
// This applies to both containers and document nodes with children
|
|
369
|
+
if (node.expanded && !showZeroDcmts && allChildrenHidden) {
|
|
370
|
+
// Add info message at the beginning, keep original children (hidden but preserved)
|
|
371
|
+
updatedItems = [
|
|
372
|
+
{
|
|
373
|
+
key: `__info__${node.key}`,
|
|
374
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
375
|
+
isContainer: false,
|
|
376
|
+
isDcmt: false,
|
|
377
|
+
isInfoMessage: true,
|
|
378
|
+
isExpandible: false
|
|
379
|
+
},
|
|
380
|
+
...filteredChildren
|
|
381
|
+
];
|
|
382
|
+
// Don't hide the node itself if it's showing an info message
|
|
383
|
+
shouldHide = false;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
updatedItems = filteredChildren;
|
|
387
|
+
// Hide zero-item containers when showZeroDcmts is false (only if not showing info message)
|
|
388
|
+
shouldHide = !showZeroDcmts && (node.isZero ?? false);
|
|
389
|
+
}
|
|
334
390
|
}
|
|
335
|
-
|
|
391
|
+
else {
|
|
392
|
+
// No items, apply normal hide logic
|
|
393
|
+
shouldHide = !showZeroDcmts && (node.isZero ?? false);
|
|
394
|
+
}
|
|
395
|
+
// IMPORTANT: Always create new object to trigger React re-render
|
|
396
|
+
return { ...node, hidden: shouldHide, items: updatedItems };
|
|
336
397
|
});
|
|
337
398
|
}, [showZeroDcmts]);
|
|
399
|
+
/**
|
|
400
|
+
* Helper function to set up initial tree expansion state.
|
|
401
|
+
* Called after all data is loaded but before setTreeData.
|
|
402
|
+
* Ensures: root container expanded, first document expanded with isRoot=true (for focus),
|
|
403
|
+
* first correlation folder expanded ONLY if it's the only one (single correlation type)
|
|
404
|
+
* Returns a NEW tree with the modifications (immutable approach)
|
|
405
|
+
*/
|
|
406
|
+
const setupInitialTreeExpansion = (tree) => {
|
|
407
|
+
if (tree.length === 0)
|
|
408
|
+
return tree;
|
|
409
|
+
// ALWAYS expand the first container (even if empty, to show infoMessage)
|
|
410
|
+
const firstRootContainer = tree[0];
|
|
411
|
+
if (!firstRootContainer)
|
|
412
|
+
return tree;
|
|
413
|
+
// Create a deep copy of the first container with expanded=true, isRoot=true
|
|
414
|
+
let newFirstContainer = {
|
|
415
|
+
...firstRootContainer,
|
|
416
|
+
expanded: true,
|
|
417
|
+
isRoot: true
|
|
418
|
+
};
|
|
419
|
+
// 2. Find first document/container and expand it
|
|
420
|
+
const firstRootItems = newFirstContainer.items;
|
|
421
|
+
if (firstRootItems && firstRootItems.length > 0) {
|
|
422
|
+
const firstDocOrContainer = firstRootItems[0];
|
|
423
|
+
if (firstDocOrContainer.isDcmt) {
|
|
424
|
+
// First item is a document - expand it and mark as root for focus
|
|
425
|
+
let newFirstDoc = {
|
|
426
|
+
...firstDocOrContainer,
|
|
427
|
+
expanded: true,
|
|
428
|
+
isRoot: true
|
|
429
|
+
};
|
|
430
|
+
// 3. Expand first correlation folder ONLY if there's exactly one
|
|
431
|
+
const docItems = newFirstDoc.items;
|
|
432
|
+
const containerChildren = docItems?.filter(child => child.isContainer) ?? [];
|
|
433
|
+
if (containerChildren.length === 1 && docItems && docItems.length > 0 && docItems[0].isContainer) {
|
|
434
|
+
const newFirstCorrelation = { ...docItems[0], expanded: true };
|
|
435
|
+
newFirstDoc = {
|
|
436
|
+
...newFirstDoc,
|
|
437
|
+
items: [newFirstCorrelation, ...docItems.slice(1)]
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
// Update the container's items
|
|
441
|
+
newFirstContainer = {
|
|
442
|
+
...newFirstContainer,
|
|
443
|
+
items: [newFirstDoc, ...firstRootItems.slice(1)]
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
else if (firstDocOrContainer.isContainer) {
|
|
447
|
+
// First item is a container (correlation folder)
|
|
448
|
+
// Count how many sibling containers there are
|
|
449
|
+
const siblingContainers = firstRootItems.filter(child => child.isContainer);
|
|
450
|
+
const shouldExpand = siblingContainers.length === 1;
|
|
451
|
+
let newFirstCorrelation = {
|
|
452
|
+
...firstDocOrContainer,
|
|
453
|
+
expanded: shouldExpand
|
|
454
|
+
};
|
|
455
|
+
// Find first document inside this container and mark for focus
|
|
456
|
+
const containerItems = newFirstCorrelation.items;
|
|
457
|
+
if (containerItems && containerItems.length > 0 && containerItems[0].isDcmt) {
|
|
458
|
+
const newFirstDoc = { ...containerItems[0], isRoot: true };
|
|
459
|
+
newFirstCorrelation = {
|
|
460
|
+
...newFirstCorrelation,
|
|
461
|
+
items: [newFirstDoc, ...containerItems.slice(1)]
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
// Update the container's items
|
|
465
|
+
newFirstContainer = {
|
|
466
|
+
...newFirstContainer,
|
|
467
|
+
items: [newFirstCorrelation, ...firstRootItems.slice(1)]
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
// If firstRootItems is empty/undefined, the container is still expanded (will show infoMessage)
|
|
472
|
+
// Return new tree with the modified first container
|
|
473
|
+
return [newFirstContainer, ...tree.slice(1)];
|
|
474
|
+
};
|
|
338
475
|
/**
|
|
339
476
|
* Main data loading function
|
|
340
477
|
*/
|
|
@@ -385,8 +522,25 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
385
522
|
}
|
|
386
523
|
}
|
|
387
524
|
}
|
|
388
|
-
// Aggiungi i master come root
|
|
389
|
-
|
|
525
|
+
// Aggiungi i master come root, raggruppando per TID e relationId
|
|
526
|
+
for (const masterContainer of masterDocs) {
|
|
527
|
+
const existingContainer = tree.find(c => c.tid === masterContainer.tid && c.relationId === masterContainer.relationId && c.isContainer);
|
|
528
|
+
if (existingContainer) {
|
|
529
|
+
// Merge documents into existing container, deduplicating by DID
|
|
530
|
+
const existingItems = existingContainer.items ?? [];
|
|
531
|
+
const existingDIDs = new Set(existingItems.map(item => item.did));
|
|
532
|
+
const newItems = (masterContainer.items ?? []).filter(item => !existingDIDs.has(item.did));
|
|
533
|
+
existingContainer.items = [...existingItems, ...newItems];
|
|
534
|
+
existingContainer.itemsCount = existingContainer.items.length;
|
|
535
|
+
existingContainer.isZero = existingContainer.items.length === 0;
|
|
536
|
+
if (masterContainer.searchResult) {
|
|
537
|
+
existingContainer.searchResult = [...(existingContainer.searchResult ?? []), ...masterContainer.searchResult];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
tree.push(masterContainer);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
390
544
|
}
|
|
391
545
|
// ========================================================================
|
|
392
546
|
// MODALITÀ STANDARD (detail o master invertito)
|
|
@@ -398,23 +552,53 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
398
552
|
: await getDetailDcmtsAsync(dcmt.TID, dcmt.DID, maxDepthLevel);
|
|
399
553
|
// Se showMainDocument è false, mostra i container dei dettagli direttamente sotto il container principale
|
|
400
554
|
if (!showMainDocument) {
|
|
401
|
-
//
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
555
|
+
// Check if a container for this TID already exists in the tree
|
|
556
|
+
const existingContainer = tree.find(c => c.tid === dcmt.TID && c.isContainer);
|
|
557
|
+
if (existingContainer) {
|
|
558
|
+
// Merge relatedDocs into existing container, grouping sub-containers by TID+relationId
|
|
559
|
+
const existingItems = existingContainer.items ?? [];
|
|
560
|
+
for (const relDoc of relatedDocs) {
|
|
561
|
+
const existingSub = existingItems.find(s => s.tid === relDoc.tid && s.relationId === relDoc.relationId && s.isContainer);
|
|
562
|
+
if (existingSub) {
|
|
563
|
+
// Merge documents into existing sub-container, deduplicating by DID
|
|
564
|
+
const subItems = existingSub.items ?? [];
|
|
565
|
+
const existingDIDs = new Set(subItems.map(item => item.did));
|
|
566
|
+
const newItems = (relDoc.items ?? []).filter(item => !existingDIDs.has(item.did));
|
|
567
|
+
existingSub.items = [...subItems, ...newItems];
|
|
568
|
+
existingSub.itemsCount = existingSub.items.length;
|
|
569
|
+
existingSub.isZero = existingSub.items.length === 0;
|
|
570
|
+
if (relDoc.searchResult) {
|
|
571
|
+
existingSub.searchResult = [...(existingSub.searchResult ?? []), ...relDoc.searchResult];
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
existingItems.push(relDoc);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
existingContainer.items = existingItems;
|
|
579
|
+
existingContainer.itemsCount = existingItems.length;
|
|
580
|
+
if (result) {
|
|
581
|
+
existingContainer.searchResult = [...(existingContainer.searchResult ?? []), result];
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
// Create type container that contains detail containers directly
|
|
586
|
+
const typeContainer = {
|
|
587
|
+
key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
|
|
588
|
+
name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
|
|
589
|
+
tid: dcmt.TID,
|
|
590
|
+
dtd,
|
|
591
|
+
isContainer: true,
|
|
592
|
+
isLoaded: true,
|
|
593
|
+
isZero: false,
|
|
594
|
+
searchResult: result ? [result] : [],
|
|
595
|
+
items: relatedDocs, // Directly show detail containers
|
|
596
|
+
itemsCount: relatedDocs.length,
|
|
597
|
+
expanded: false,
|
|
598
|
+
hidden: false
|
|
599
|
+
};
|
|
600
|
+
tree.push(typeContainer);
|
|
601
|
+
}
|
|
418
602
|
}
|
|
419
603
|
else {
|
|
420
604
|
// MODALITÀ ORIGINALE: mostra il documento master come nodo intermedio
|
|
@@ -426,10 +610,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
426
610
|
did: dcmt.DID,
|
|
427
611
|
isDcmt: true,
|
|
428
612
|
isContainer: false,
|
|
429
|
-
expanded:
|
|
613
|
+
expanded: false,
|
|
430
614
|
isZero: dcmt.DID === 0,
|
|
431
615
|
isMaster: !isForMaster,
|
|
432
|
-
isRoot: true,
|
|
433
616
|
isLoaded: true,
|
|
434
617
|
hidden: false,
|
|
435
618
|
values: docRow,
|
|
@@ -437,31 +620,90 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
437
620
|
items: relatedDocs,
|
|
438
621
|
itemsCount: relatedDocs.length
|
|
439
622
|
};
|
|
440
|
-
//
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
623
|
+
// Check if a type container for this TID already exists in the tree
|
|
624
|
+
const existingContainer = tree.find(c => c.tid === dcmt.TID && c.isContainer);
|
|
625
|
+
if (existingContainer) {
|
|
626
|
+
// Add document to existing container
|
|
627
|
+
const existingItems = existingContainer.items ?? [];
|
|
628
|
+
existingContainer.items = [...existingItems, docNode];
|
|
629
|
+
existingContainer.itemsCount = existingContainer.items.length;
|
|
630
|
+
if (result) {
|
|
631
|
+
existingContainer.searchResult = [...(existingContainer.searchResult ?? []), result];
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
// Create type container with unique key
|
|
636
|
+
const typeContainer = {
|
|
637
|
+
key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
|
|
638
|
+
name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
|
|
639
|
+
tid: dcmt.TID,
|
|
640
|
+
dtd,
|
|
641
|
+
isContainer: true,
|
|
642
|
+
isLoaded: true,
|
|
643
|
+
isZero: false, // Type container is never zero (contains documents)
|
|
644
|
+
searchResult: result ? [result] : [],
|
|
645
|
+
items: [docNode],
|
|
646
|
+
itemsCount: 1,
|
|
647
|
+
expanded: false,
|
|
648
|
+
hidden: false
|
|
649
|
+
};
|
|
650
|
+
tree.push(typeContainer);
|
|
651
|
+
}
|
|
457
652
|
}
|
|
458
653
|
}
|
|
459
654
|
// Update progress counter
|
|
460
655
|
processedCount++;
|
|
461
656
|
setWaitPanelValuePrimary(processedCount);
|
|
462
657
|
}
|
|
463
|
-
|
|
464
|
-
|
|
658
|
+
/**
|
|
659
|
+
* Helper function to check if a node has any actual correlated documents
|
|
660
|
+
* Returns true if there are NO documents (relations are empty)
|
|
661
|
+
*/
|
|
662
|
+
const hasNoActualDocuments = (items) => {
|
|
663
|
+
if (!items || items.length === 0)
|
|
664
|
+
return true;
|
|
665
|
+
// Check each item
|
|
666
|
+
return items.every(item => {
|
|
667
|
+
// If it's a container, check if it's zero or has no documents
|
|
668
|
+
if (item.isContainer) {
|
|
669
|
+
// Container with isZero=true has no documents
|
|
670
|
+
if (item.isZero)
|
|
671
|
+
return true;
|
|
672
|
+
// Container with no items has no documents
|
|
673
|
+
if (!item.items || item.items.length === 0)
|
|
674
|
+
return true;
|
|
675
|
+
// Otherwise, it has documents
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
678
|
+
// If it's a document node, check its children (relation containers)
|
|
679
|
+
if (item.isDcmt) {
|
|
680
|
+
// Check if all child containers are empty
|
|
681
|
+
return hasNoActualDocuments(item.items);
|
|
682
|
+
}
|
|
683
|
+
// For other types, assume no documents
|
|
684
|
+
return true;
|
|
685
|
+
});
|
|
686
|
+
};
|
|
687
|
+
// Check if there are no relations for any document
|
|
688
|
+
const hasNoRelations = tree.length === 0 || tree.every(container => {
|
|
689
|
+
// Check if container has no items
|
|
690
|
+
if (!container.items || container.items.length === 0)
|
|
691
|
+
return true;
|
|
692
|
+
// Check recursively if there are any actual documents
|
|
693
|
+
return hasNoActualDocuments(container.items);
|
|
694
|
+
});
|
|
695
|
+
// If no relations found, notify parent
|
|
696
|
+
if (hasNoRelations && onNoRelationsFound) {
|
|
697
|
+
onNoRelationsFound();
|
|
698
|
+
}
|
|
699
|
+
// FIRST setup initial expansion state (root container expanded, first document expanded with focus, first correlation folder expanded)
|
|
700
|
+
// This must run BEFORE updateHiddenProperty so that infoMessage logic sees expanded=true
|
|
701
|
+
const expandedTree = setupInitialTreeExpansion(tree);
|
|
702
|
+
// THEN apply hidden property transformations (creates new objects, adds infoMessage where needed)
|
|
703
|
+
// Now it will correctly detect expanded nodes and add infoMessage if all children are hidden
|
|
704
|
+
const processedTree = updateHiddenProperty(expandedTree);
|
|
705
|
+
setTreeData(processedTree);
|
|
706
|
+
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer, onNoRelationsFound]);
|
|
465
707
|
/**
|
|
466
708
|
* Merge main tree data with additional static items
|
|
467
709
|
*/
|
|
@@ -483,16 +725,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
483
725
|
if (!inputDcmts || inputDcmts.length === 0 || dcmtTypes.length === 0) {
|
|
484
726
|
setTreeData([]);
|
|
485
727
|
lastLoadedInputRef.current = '';
|
|
728
|
+
lastFocusedInputRef.current = '';
|
|
729
|
+
setLoadedInputKey('');
|
|
486
730
|
userInteractedWithStaticItemsRef.current = false; // Reset interaction flag
|
|
487
731
|
return;
|
|
488
732
|
}
|
|
489
733
|
// Generate current input key
|
|
490
734
|
const currentKey = getInputKey();
|
|
491
|
-
// Skip if we already loaded this exact data
|
|
735
|
+
// Skip if we already loaded or are loading this exact data
|
|
492
736
|
if (currentKey === lastLoadedInputRef.current && treeData.length > 0) {
|
|
493
737
|
return;
|
|
494
738
|
}
|
|
495
|
-
// Mark as loading this key
|
|
739
|
+
// Mark as loading this key to prevent duplicate loads
|
|
496
740
|
lastLoadedInputRef.current = currentKey;
|
|
497
741
|
// Reset interaction flag when loading new data
|
|
498
742
|
userInteractedWithStaticItemsRef.current = false;
|
|
@@ -501,6 +745,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
501
745
|
setWaitPanelValuePrimary(0);
|
|
502
746
|
// Call loadData and use .then() instead of await to allow React to render
|
|
503
747
|
loadData().then(() => {
|
|
748
|
+
// Mark as loaded AFTER data is ready - state update triggers re-render for focus selection
|
|
749
|
+
lastLoadedInputRef.current = currentKey;
|
|
750
|
+
setLoadedInputKey(currentKey);
|
|
504
751
|
setShowWaitPanel(false);
|
|
505
752
|
setWaitPanelTextPrimary('');
|
|
506
753
|
setWaitPanelMaxValuePrimary(0);
|
|
@@ -509,10 +756,53 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
509
756
|
}, [inputDcmts, dcmtTypes, maxDepthLevel, getInputKey, loadData, treeData.length]);
|
|
510
757
|
/**
|
|
511
758
|
* Update tree when showZeroDcmts changes
|
|
759
|
+
* Updates the visualization without re-fetching data (isLoaded stays true)
|
|
512
760
|
*/
|
|
513
761
|
useEffect(() => {
|
|
514
762
|
setTreeData(prevData => updateHiddenProperty(prevData));
|
|
515
763
|
}, [showZeroDcmts, updateHiddenProperty]);
|
|
764
|
+
/**
|
|
765
|
+
* Set focused item when data finishes loading
|
|
766
|
+
* Focuses on the first document (under root) every time new data is loaded
|
|
767
|
+
* Works both on initial load and on navigation (onPrev/onNext)
|
|
768
|
+
*/
|
|
769
|
+
useEffect(() => {
|
|
770
|
+
const currentInputKey = getInputKey();
|
|
771
|
+
// Ensure data has finished loading for current input
|
|
772
|
+
if (loadedInputKey !== currentInputKey) {
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (!showMainDocument || !onFocusedItemChanged || !treeData.length || !inputDcmts?.length) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
// Skip if we already focused for this inputKey
|
|
779
|
+
if (lastFocusedInputRef.current === currentInputKey) {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
// Helper function to recursively find the first document with isRoot=true
|
|
783
|
+
const findFirstRootDocument = (items) => {
|
|
784
|
+
for (const item of items) {
|
|
785
|
+
// Check if this item is a document with isRoot=true
|
|
786
|
+
if (item.isDcmt && item.isRoot) {
|
|
787
|
+
return item;
|
|
788
|
+
}
|
|
789
|
+
// Recursively search in children
|
|
790
|
+
if (item.items && Array.isArray(item.items)) {
|
|
791
|
+
const found = findFirstRootDocument(item.items);
|
|
792
|
+
if (found)
|
|
793
|
+
return found;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
};
|
|
798
|
+
// Find the first document marked as root (set by setupInitialTreeExpansion)
|
|
799
|
+
const docNode = findFirstRootDocument(treeData);
|
|
800
|
+
if (docNode) {
|
|
801
|
+
// Set the focused item and mark this inputKey as focused
|
|
802
|
+
onFocusedItemChanged(docNode);
|
|
803
|
+
lastFocusedInputRef.current = currentInputKey;
|
|
804
|
+
}
|
|
805
|
+
}, [treeData, loadedInputKey, showMainDocument, onFocusedItemChanged, inputDcmts, getInputKey]);
|
|
516
806
|
/**
|
|
517
807
|
* Sync static items state when additionalStaticItems change
|
|
518
808
|
* IMPORTANT: Only update if user hasn't manually interacted with the tree,
|
|
@@ -548,6 +838,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
548
838
|
/**
|
|
549
839
|
* Calculate items for node when expanded (lazy loading)
|
|
550
840
|
* Note: additionalStaticItems are already fully loaded, so skip calculation for them
|
|
841
|
+
*
|
|
842
|
+
* PERFORMANCE OPTIMIZATION:
|
|
843
|
+
* - Containers: Return items immediately (no API calls) - items are already loaded
|
|
844
|
+
* - Documents: Load children lazily only when the specific document is expanded (1 API call)
|
|
551
845
|
*/
|
|
552
846
|
const calculateItemsForNode = useCallback(async (node) => {
|
|
553
847
|
// Skip calculation for separator (it has no children)
|
|
@@ -558,62 +852,129 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
558
852
|
if (node.isStaticItem || node.isAdditionalContainer || node.isAdditional) {
|
|
559
853
|
return node.items;
|
|
560
854
|
}
|
|
561
|
-
// If
|
|
562
|
-
if (node.
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
processedCount++;
|
|
586
|
-
setExpansionWaitPanelValue(processedCount);
|
|
587
|
-
setExpansionWaitPanelText(`Caricamento ${processedCount} di ${itemsToLoad}...`);
|
|
588
|
-
// Nella modalità originale (invertMasterNavigation=false),
|
|
589
|
-
// i documenti detail non devono caricare i master come figli
|
|
590
|
-
if (isForMaster && !invertMasterNavigation) {
|
|
591
|
-
// Carica i detail dei detail (navigazione naturale detail→detail)
|
|
592
|
-
const loadedItems = await getDetailDcmtsAsync(item.tid, item.did, 1);
|
|
593
|
-
item.items = updateHiddenProperty(loadedItems);
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
// Modalità standard o invertita
|
|
597
|
-
const loadedItems = isForMaster
|
|
598
|
-
? await getMasterDcmtsAsync(item.tid, item.did, 1)
|
|
599
|
-
: await getDetailDcmtsAsync(item.tid, item.did, 1);
|
|
600
|
-
item.items = updateHiddenProperty(loadedItems);
|
|
601
|
-
}
|
|
602
|
-
item.isLoaded = true;
|
|
855
|
+
// If already loaded, apply current visualization logic without re-fetching
|
|
856
|
+
if (node.isLoaded && node.items) {
|
|
857
|
+
// Apply current show/hide logic based on showZeroDcmts
|
|
858
|
+
const updatedItems = updateHiddenProperty(node.items);
|
|
859
|
+
// If this is a container, check if we need to show info message
|
|
860
|
+
if (node.isContainer) {
|
|
861
|
+
// Remove any existing info messages
|
|
862
|
+
const filteredItems = updatedItems.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
863
|
+
// Check if all real children are hidden
|
|
864
|
+
const realChildren = filteredItems.filter(child => !child.isInfoMessage);
|
|
865
|
+
const allChildrenHidden = realChildren.length > 0 && realChildren.every(child => child.hidden);
|
|
866
|
+
// If showZeroDcmts is false and all items are hidden, show info message + keep hidden containers
|
|
867
|
+
if (!showZeroDcmts && allChildrenHidden) {
|
|
868
|
+
return [
|
|
869
|
+
{
|
|
870
|
+
key: `__info__${node.key}`,
|
|
871
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
872
|
+
isContainer: false,
|
|
873
|
+
isDcmt: false,
|
|
874
|
+
isInfoMessage: true,
|
|
875
|
+
isExpandible: false
|
|
876
|
+
},
|
|
877
|
+
...filteredItems // Keep hidden containers so they can be shown when toggling
|
|
878
|
+
];
|
|
603
879
|
}
|
|
604
|
-
newItems.push(item);
|
|
605
880
|
}
|
|
606
|
-
return
|
|
881
|
+
return updatedItems;
|
|
607
882
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
883
|
+
// ============================================
|
|
884
|
+
// CONTAINER: Show items immediately (no API calls)
|
|
885
|
+
// Items are already loaded from initial load or parent expansion
|
|
886
|
+
// ============================================
|
|
887
|
+
if (node.isContainer) {
|
|
888
|
+
// No API calls needed - just return existing items
|
|
889
|
+
// Children of these items will be loaded lazily when user expands each document
|
|
890
|
+
// Apply updateHiddenProperty to respect current showZeroDcmts setting
|
|
891
|
+
// If node has no items, return empty array
|
|
892
|
+
if (!node.items)
|
|
893
|
+
return [];
|
|
894
|
+
const updatedItems = updateHiddenProperty(node.items);
|
|
895
|
+
// If showZeroDcmts is false and all items are hidden, add info message + keep hidden containers
|
|
896
|
+
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
897
|
+
return [
|
|
898
|
+
{
|
|
899
|
+
key: `__info__${node.key}`,
|
|
900
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
901
|
+
isContainer: false,
|
|
902
|
+
isDcmt: false,
|
|
903
|
+
isInfoMessage: true,
|
|
904
|
+
isExpandible: false
|
|
905
|
+
},
|
|
906
|
+
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
907
|
+
];
|
|
908
|
+
}
|
|
909
|
+
return updatedItems;
|
|
611
910
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
911
|
+
// ============================================
|
|
912
|
+
// DOCUMENT: Load its children (relation containers) lazily
|
|
913
|
+
// Only makes ONE API call for this specific document
|
|
914
|
+
// ============================================
|
|
915
|
+
if (node.isDcmt && node.tid && node.did) {
|
|
916
|
+
const newAbortController = new AbortController();
|
|
917
|
+
setExpansionAbortController(newAbortController);
|
|
918
|
+
setShowExpansionWaitPanel(true);
|
|
919
|
+
setExpansionWaitPanelMaxValue(1);
|
|
920
|
+
setExpansionWaitPanelValue(0);
|
|
921
|
+
setExpansionWaitPanelText(`Caricamento documenti correlati...`);
|
|
922
|
+
try {
|
|
923
|
+
// Check for abort
|
|
924
|
+
if (newAbortController.signal.aborted) {
|
|
925
|
+
return [];
|
|
926
|
+
}
|
|
927
|
+
// Determine which load function to use based on mode
|
|
928
|
+
let loadedItems = [];
|
|
929
|
+
if (isForMaster && !invertMasterNavigation) {
|
|
930
|
+
// Original mode: detail documents load detail documents (natural detail→detail navigation)
|
|
931
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
932
|
+
}
|
|
933
|
+
else if (isForMaster) {
|
|
934
|
+
// Inverted master mode: load master documents
|
|
935
|
+
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1);
|
|
936
|
+
}
|
|
937
|
+
else {
|
|
938
|
+
// Standard mode: load detail documents
|
|
939
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
940
|
+
}
|
|
941
|
+
// Espandi automaticamente il primo container se ci sono documenti correlati
|
|
942
|
+
if (loadedItems.length > 0 && loadedItems[0].isContainer) {
|
|
943
|
+
loadedItems[0].expanded = true;
|
|
944
|
+
}
|
|
945
|
+
// Apply updateHiddenProperty to respect current showZeroDcmts setting
|
|
946
|
+
// This ensures that dynamically loaded nodes respect the visibility rules:
|
|
947
|
+
// - Relation type containers are always visible (even if empty)
|
|
948
|
+
// - Other containers with zero items can be hidden based on showZeroDcmts
|
|
949
|
+
const updatedItems = updateHiddenProperty(loadedItems);
|
|
950
|
+
// If showZeroDcmts is false and all items are hidden, add info message + keep hidden containers
|
|
951
|
+
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
952
|
+
return [
|
|
953
|
+
{
|
|
954
|
+
key: `__info__${node.key}`,
|
|
955
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
956
|
+
isContainer: false,
|
|
957
|
+
isDcmt: false,
|
|
958
|
+
isInfoMessage: true,
|
|
959
|
+
isExpandible: false
|
|
960
|
+
},
|
|
961
|
+
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
962
|
+
];
|
|
963
|
+
}
|
|
964
|
+
return updatedItems;
|
|
965
|
+
}
|
|
966
|
+
catch (error) {
|
|
967
|
+
console.error('Error loading document relations:', error);
|
|
968
|
+
return [];
|
|
969
|
+
}
|
|
970
|
+
finally {
|
|
971
|
+
setShowExpansionWaitPanel(false);
|
|
972
|
+
setExpansionAbortController(undefined);
|
|
973
|
+
}
|
|
615
974
|
}
|
|
616
|
-
|
|
975
|
+
// Default: return existing items
|
|
976
|
+
return node.items;
|
|
977
|
+
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, showZeroDcmts]);
|
|
617
978
|
/**
|
|
618
979
|
* Default item renderer with metadata display (adapted from TMMasterDetailDcmts.tsx)
|
|
619
980
|
*/
|
|
@@ -626,6 +987,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
626
987
|
e.stopPropagation();
|
|
627
988
|
onDocumentDoubleClick?.(item.tid, item.did, item.name);
|
|
628
989
|
};
|
|
990
|
+
// Info message rendering
|
|
991
|
+
if (item.isInfoMessage) {
|
|
992
|
+
return (_jsxs("div", { style: {
|
|
993
|
+
display: 'flex',
|
|
994
|
+
alignItems: 'center',
|
|
995
|
+
gap: '10px',
|
|
996
|
+
height: '32px',
|
|
997
|
+
padding: '6px 0',
|
|
998
|
+
color: '#666',
|
|
999
|
+
fontStyle: 'italic'
|
|
1000
|
+
}, children: [_jsx(IconCircleInfo, { fontSize: 20, color: TMColors.iconLight }), _jsx("span", { children: item.name })] }));
|
|
1001
|
+
}
|
|
629
1002
|
// Container rendering
|
|
630
1003
|
if (item.isContainer || !item.isDcmt) {
|
|
631
1004
|
const defaultContainerStyle = {
|
|
@@ -634,7 +1007,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
634
1007
|
gap: '10px',
|
|
635
1008
|
height: '32px',
|
|
636
1009
|
padding: '6px 0',
|
|
637
|
-
opacity: item.isZero ? 0.
|
|
1010
|
+
opacity: item.isZero ? 0.99 : 1,
|
|
638
1011
|
transition: 'opacity 0.2s ease-in-out'
|
|
639
1012
|
};
|
|
640
1013
|
// Se è il container principale (root) e showMainDocument è false,
|
|
@@ -653,6 +1026,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
653
1026
|
return (_jsx("div", { style: defaultContainerStyle, children: content }));
|
|
654
1027
|
}
|
|
655
1028
|
// Document rendering with full metadata display
|
|
1029
|
+
const isLogicallyDeleted = Number(item.isLogDel) === 1;
|
|
656
1030
|
const defaultDocumentStyle = {
|
|
657
1031
|
minWidth: '90px',
|
|
658
1032
|
width: '100%',
|
|
@@ -664,18 +1038,23 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
664
1038
|
alignItems: 'center',
|
|
665
1039
|
cursor: 'pointer',
|
|
666
1040
|
userSelect: 'none',
|
|
667
|
-
opacity: item.isZero ? 0.
|
|
668
|
-
transition: 'opacity 0.2s ease-in-out'
|
|
1041
|
+
opacity: item.isZero ? 0.99 : 1,
|
|
1042
|
+
transition: 'opacity 0.2s ease-in-out',
|
|
1043
|
+
textDecoration: isLogicallyDeleted ? 'line-through' : 'none'
|
|
669
1044
|
};
|
|
670
1045
|
const documentStyle = customDocumentStyle
|
|
671
1046
|
? { ...defaultDocumentStyle, ...customDocumentStyle(item) }
|
|
672
1047
|
: defaultDocumentStyle;
|
|
1048
|
+
const textDecoration = isLogicallyDeleted ? 'line-through' : 'none';
|
|
1049
|
+
const textColor = isLogicallyDeleted ? 'gray' : undefined;
|
|
673
1050
|
const defaultMetadataContent = item.values && (_jsx(StyledDivHorizontal, { style: {
|
|
674
1051
|
fontSize: '1rem',
|
|
675
1052
|
overflow: 'hidden',
|
|
676
1053
|
flex: 1,
|
|
677
1054
|
minWidth: 0,
|
|
678
|
-
whiteSpace: 'nowrap'
|
|
1055
|
+
whiteSpace: 'nowrap',
|
|
1056
|
+
textDecoration: textDecoration,
|
|
1057
|
+
color: textColor
|
|
679
1058
|
}, children: getDcmtDisplayValue(item.values).map((key, index) => {
|
|
680
1059
|
const md = item.values?.[key]?.md;
|
|
681
1060
|
const value = item.values?.[key]?.value;
|
|
@@ -684,18 +1063,22 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
684
1063
|
return (_jsxs(StyledDivHorizontal, { style: {
|
|
685
1064
|
flexShrink: isLast ? 1 : 0,
|
|
686
1065
|
minWidth: isLast ? 0 : 'auto',
|
|
687
|
-
overflow: isLast ? 'hidden' : 'visible'
|
|
688
|
-
|
|
1066
|
+
overflow: isLast ? 'hidden' : 'visible',
|
|
1067
|
+
textDecoration: textDecoration,
|
|
1068
|
+
color: textColor
|
|
1069
|
+
}, children: [index > 0 && _jsx("span", { style: { margin: '0 5px', color: textColor || '#999', textDecoration: textDecoration }, children: "\u2022" }), showMetadataNames && (_jsxs("span", { style: { color: textColor || '#666', marginRight: '5px', textDecoration: textDecoration }, children: [md?.name || key, ":"] })), md?.dataDomain === MetadataDataDomains.DataList ? (_jsx("span", { style: { textDecoration: textDecoration, color: textColor }, children: _jsx(TMDataListItemViewer, { dataListId: md.dataListID, viewMode: md.dataListViewMode, value: value }) })) : md?.dataDomain === MetadataDataDomains.UserID ? (_jsx("span", { style: { textDecoration: textDecoration, color: textColor }, children: _jsx(TMDataUserIdItemViewer, { userId: value, showIcon: true }) })) : (_jsx("span", { style: {
|
|
689
1070
|
fontWeight: 500,
|
|
690
1071
|
overflow: isLast ? 'hidden' : 'visible',
|
|
691
1072
|
textOverflow: isLast ? 'ellipsis' : 'clip',
|
|
692
|
-
whiteSpace: 'nowrap'
|
|
1073
|
+
whiteSpace: 'nowrap',
|
|
1074
|
+
textDecoration: textDecoration,
|
|
1075
|
+
color: textColor
|
|
693
1076
|
}, children: value }))] }, `${key}_${index}`));
|
|
694
1077
|
}) }));
|
|
695
1078
|
const metadataContent = customDocumentContent
|
|
696
1079
|
? customDocumentContent(item, defaultMetadataContent || _jsx(_Fragment, {}))
|
|
697
1080
|
: defaultMetadataContent;
|
|
698
|
-
return (_jsxs("div", { onDoubleClick: handleDoubleClick, style: documentStyle, children: [item.did && showCurrentDcmtIndicator && item.did ===
|
|
1081
|
+
return (_jsxs("div", { onDoubleClick: handleDoubleClick, style: documentStyle, children: [item.did && item.tid && showCurrentDcmtIndicator && inputDcmts?.some(d => d.DID === item.did && d.TID === item.tid) ? _jsx(IconBackhandIndexPointingRight, { fontSize: 22, overflow: 'visible' }) : _jsx(_Fragment, {}), item.values && (_jsx(TMDcmtIcon, { tid: item.values?.TID?.value, did: item.values?.DID?.value, fileExtension: item.values?.FILEEXT?.value, fileCount: item.values?.FILECOUNT?.value, isLexProt: item.values?.IsLexProt?.value, isMail: item.values?.ISMAIL?.value, isShared: item.values?.ISSHARED?.value, isSigned: item.values?.ISSIGNED?.value, downloadMode: 'openInNewWindow' })), metadataContent] }));
|
|
699
1082
|
}, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument]);
|
|
700
1083
|
/**
|
|
701
1084
|
* Wrapper renderer that handles custom rendering if provided
|
|
@@ -742,9 +1125,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
742
1125
|
return _jsx("div", { style: { padding: '20px', textAlign: 'center' }, children: _jsx(TMWaitPanel, { title: 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: waitPanelTextPrimary, valuePrimary: waitPanelValuePrimary, maxValuePrimary: waitPanelMaxValuePrimary, isCancelable: true, abortController: abortController, onAbortClick: (abortController) => { setTimeout(() => { abortController?.abort(); }, 1000); } }) });
|
|
743
1126
|
}
|
|
744
1127
|
if (mergedTreeData.length === 0) {
|
|
1128
|
+
// If parent handles no-relations via callback, render nothing to avoid UI flash
|
|
1129
|
+
if (onNoRelationsFound)
|
|
1130
|
+
return null;
|
|
745
1131
|
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
|
|
746
1132
|
}
|
|
747
|
-
return (_jsxs(_Fragment, { children: [_jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, onSelectionChanged: handleSelectedItemsChanged }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? 'Caricamento documenti master' : 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: (abortController) => {
|
|
1133
|
+
return (_jsxs(_Fragment, { children: [_jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, itemsPerPage: 100, onSelectionChanged: handleSelectedItemsChanged }), showExpansionWaitPanel && (_jsx(TMWaitPanel, { title: isForMaster ? 'Caricamento documenti master' : 'Caricamento documenti dettaglio', showPrimary: true, textPrimary: expansionWaitPanelText, valuePrimary: expansionWaitPanelValue, maxValuePrimary: expansionWaitPanelMaxValue, isCancelable: true, abortController: expansionAbortController, onAbortClick: (abortController) => {
|
|
748
1134
|
setTimeout(() => {
|
|
749
1135
|
abortController?.abort();
|
|
750
1136
|
}, 100);
|