@topconsultnpm/sdkui-react 6.20.0-test1 → 6.20.0

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 (109) hide show
  1. package/lib/components/NewComponents/ContextMenu/styles.d.ts +3 -1
  2. package/lib/components/NewComponents/ContextMenu/styles.js +7 -5
  3. package/lib/components/base/Styled.d.ts +4 -1
  4. package/lib/components/base/Styled.js +11 -3
  5. package/lib/components/base/TMPanel.js +6 -4
  6. package/lib/components/base/TMPopUp.js +4 -0
  7. package/lib/components/base/TMTreeView.d.ts +3 -1
  8. package/lib/components/base/TMTreeView.js +68 -21
  9. package/lib/components/choosers/TMDataListItemChooser.js +1 -1
  10. package/lib/components/choosers/TMDataListItemEditor.d.ts +11 -0
  11. package/lib/components/choosers/TMDataListItemEditor.js +130 -0
  12. package/lib/components/choosers/TMDataListItemFields.d.ts +11 -0
  13. package/lib/components/choosers/TMDataListItemFields.js +61 -0
  14. package/lib/components/choosers/TMDataListItemPicker.d.ts +2 -0
  15. package/lib/components/choosers/TMDataListItemPicker.js +182 -18
  16. package/lib/components/choosers/TMDynDataListItemChooser.js +11 -6
  17. package/lib/components/choosers/TMImageIDChooser.d.ts +16 -0
  18. package/lib/components/choosers/TMImageIDChooser.js +53 -0
  19. package/lib/components/choosers/TMMetadataChooser.js +1 -1
  20. package/lib/components/choosers/TMUserChooser.js +1 -1
  21. package/lib/components/editors/TMDateBox.js +1 -1
  22. package/lib/components/editors/TMHtmlEditor.js +1 -1
  23. package/lib/components/editors/TMLocalizedTextBox.d.ts +1 -0
  24. package/lib/components/editors/TMLocalizedTextBox.js +3 -3
  25. package/lib/components/editors/TMMetadataValues.js +203 -41
  26. package/lib/components/editors/TMTextArea.d.ts +1 -0
  27. package/lib/components/editors/TMTextArea.js +6 -6
  28. package/lib/components/editors/TMTextBox.js +9 -10
  29. package/lib/components/features/archive/TMArchive.d.ts +3 -1
  30. package/lib/components/features/archive/TMArchive.js +31 -44
  31. package/lib/components/features/blog/TMBlogCommentForm.d.ts +3 -0
  32. package/lib/components/features/blog/TMBlogCommentForm.js +42 -36
  33. package/lib/components/features/documents/TMDcmtForm.d.ts +3 -1
  34. package/lib/components/features/documents/TMDcmtForm.js +215 -54
  35. package/lib/components/features/documents/TMDcmtPreview.js +66 -13
  36. package/lib/components/features/documents/TMDcmtTasks.d.ts +3 -1
  37. package/lib/components/features/documents/TMDcmtTasks.js +2 -2
  38. package/lib/components/features/documents/TMFileUploader.d.ts +5 -0
  39. package/lib/components/features/documents/TMFileUploader.js +28 -6
  40. package/lib/components/features/documents/TMMasterDetailDcmts.js +31 -85
  41. package/lib/components/features/documents/TMRelationViewer.d.ts +7 -1
  42. package/lib/components/features/documents/TMRelationViewer.js +497 -111
  43. package/lib/components/features/search/TMSearchQueryPanel.js +6 -6
  44. package/lib/components/features/search/TMSearchResult.d.ts +2 -0
  45. package/lib/components/features/search/TMSearchResult.js +106 -86
  46. package/lib/components/features/search/TMSearchResultsMenuItems.d.ts +1 -1
  47. package/lib/components/features/search/TMSearchResultsMenuItems.js +6 -18
  48. package/lib/components/features/search/TMSignatureInfoContent.js +10 -6
  49. package/lib/components/features/search/TMTreeSelector.js +1 -1
  50. package/lib/components/features/tasks/TMTaskForm.d.ts +1 -0
  51. package/lib/components/features/tasks/TMTaskForm.js +61 -193
  52. package/lib/components/features/tasks/TMTaskFormUtils.d.ts +80 -0
  53. package/lib/components/features/tasks/TMTaskFormUtils.js +559 -0
  54. package/lib/components/features/tasks/TMTasksUtils.d.ts +3 -1
  55. package/lib/components/features/tasks/TMTasksUtils.js +46 -16
  56. package/lib/components/features/tasks/TMTasksUtilsView.d.ts +0 -7
  57. package/lib/components/features/tasks/TMTasksUtilsView.js +7 -14
  58. package/lib/components/features/tasks/TMTasksView.js +5 -3
  59. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +20 -3
  60. package/lib/components/features/workflow/TMWorkflowPopup.js +21 -109
  61. package/lib/components/features/workflow/diagram/ConnectionComponent.d.ts +1 -0
  62. package/lib/components/features/workflow/diagram/ConnectionComponent.js +6 -2
  63. package/lib/components/features/workflow/diagram/DiagramItemForm.d.ts +2 -0
  64. package/lib/components/features/workflow/diagram/DiagramItemForm.js +32 -25
  65. package/lib/components/features/workflow/diagram/RecipientList.d.ts +3 -1
  66. package/lib/components/features/workflow/diagram/RecipientList.js +13 -9
  67. package/lib/components/features/workflow/diagram/WFDiagram.js +102 -5
  68. package/lib/components/features/workflow/diagram/workflowHelpers.js +31 -19
  69. package/lib/components/forms/Login/TMLoginForm.js +1 -1
  70. package/lib/components/forms/TMSaveForm.js +61 -13
  71. package/lib/components/grids/TMBlogsPost.js +8 -8
  72. package/lib/components/grids/TMBlogsPostUtils.js +2 -2
  73. package/lib/components/grids/TMRecentsManager.js +1 -1
  74. package/lib/components/index.d.ts +2 -0
  75. package/lib/components/index.js +2 -0
  76. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +3 -2
  77. package/lib/components/pages/TMPage.js +4 -0
  78. package/lib/components/query/TMQueryEditor.d.ts +1 -0
  79. package/lib/components/query/TMQueryEditor.js +3 -3
  80. package/lib/components/viewers/TMMidViewer.js +2 -1
  81. package/lib/components/viewers/TMTidViewer.js +7 -3
  82. package/lib/helper/Enum_Localizator.js +5 -0
  83. package/lib/helper/GlobalStyles.js +3 -0
  84. package/lib/helper/SDKUI_Globals.d.ts +12 -0
  85. package/lib/helper/SDKUI_Globals.js +21 -1
  86. package/lib/helper/SDKUI_Localizator.d.ts +31 -7
  87. package/lib/helper/SDKUI_Localizator.js +286 -46
  88. package/lib/helper/TMIcons.d.ts +2 -1
  89. package/lib/helper/TMIcons.js +4 -1
  90. package/lib/helper/TMUtils.d.ts +33 -41
  91. package/lib/helper/TMUtils.js +157 -170
  92. package/lib/helper/helpers.d.ts +6 -2
  93. package/lib/helper/helpers.js +24 -8
  94. package/lib/helper/index.d.ts +1 -0
  95. package/lib/helper/index.js +1 -0
  96. package/lib/helper/queryHelper.js +1 -1
  97. package/lib/hooks/useBetaFeatures.d.ts +1 -0
  98. package/lib/hooks/useBetaFeatures.js +41 -0
  99. package/lib/hooks/useDataUserIdItem.js +2 -2
  100. package/lib/hooks/useDcmtOperations.js +14 -2
  101. package/lib/hooks/useRelatedDocuments.js +64 -42
  102. package/lib/index.d.ts +1 -0
  103. package/lib/index.js +1 -0
  104. package/lib/services/platform_services.d.ts +1 -1
  105. package/lib/services/platform_services.js +4 -0
  106. package/lib/ts/types.d.ts +3 -0
  107. package/package.json +2 -2
  108. package/lib/components/features/search/TMSignSettingsForm.d.ts +0 -9
  109. 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: 0,
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: 0,
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 (original simple logic)
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
- const updatedNode = { ...node, hidden: !showZeroDcmts && node.isZero };
332
- if (node.items) {
333
- updatedNode.items = updateHiddenProperty(node.items);
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
- return updatedNode;
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
- tree.push(...masterDocs);
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
- // Create type container that contains detail containers directly
402
- const typeContainer = {
403
- key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
404
- name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
405
- tid: dcmt.TID,
406
- dtd,
407
- isContainer: true,
408
- isRoot: true,
409
- isLoaded: true,
410
- isZero: false,
411
- searchResult: result ? [result] : [],
412
- items: relatedDocs, // Directly show detail containers
413
- itemsCount: relatedDocs.length,
414
- expanded: tree.length === 0,
415
- hidden: false
416
- };
417
- tree.push(typeContainer);
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: tree.length === 0,
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
- // Create type container with unique key
441
- const typeContainer = {
442
- key: `${isForMaster ? 'detail' : 'master'}-type-${dcmt.TID}-${containerGUID}`,
443
- name: labelMainContainer || result?.fromName || dtd?.name || `TID: ${dcmt.TID}`,
444
- tid: dcmt.TID,
445
- dtd,
446
- isContainer: true,
447
- isRoot: true,
448
- isLoaded: true,
449
- isZero: false, // Type container is never zero (contains documents)
450
- searchResult: result ? [result] : [],
451
- items: [docNode],
452
- itemsCount: 1,
453
- expanded: tree.length === 0,
454
- hidden: false
455
- };
456
- tree.push(typeContainer);
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
- setTreeData(updateHiddenProperty(tree));
464
- }, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer]);
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 it's a document, return existing items
562
- if (node.isDcmt)
563
- return node.items;
564
- // If container is already loaded, return items
565
- if (node.isLoaded)
566
- return node.items;
567
- const newAbortController = new AbortController();
568
- setExpansionAbortController(newAbortController);
569
- const itemsToLoad = node.items?.length ?? 0;
570
- setShowExpansionWaitPanel(true);
571
- setExpansionWaitPanelMaxValue(itemsToLoad);
572
- setExpansionWaitPanelValue(0);
573
- setExpansionWaitPanelText(`Caricamento documenti correlati...`);
574
- try {
575
- const newItems = [];
576
- let processedCount = 0;
577
- for (const dcmt of node.items ?? []) {
578
- if (newAbortController.signal.aborted) {
579
- console.log('Folder expansion aborted by user');
580
- return node.items;
581
- }
582
- const item = { ...dcmt };
583
- if (item.tid && item.did && !item.isLoaded) {
584
- // Update progress
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 newItems;
881
+ return updatedItems;
607
882
  }
608
- catch (error) {
609
- console.error('Error loading folder contents:', error);
610
- return node.items;
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
- finally {
613
- setShowExpansionWaitPanel(false);
614
- setExpansionAbortController(undefined);
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
- }, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty]);
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.4 : 1,
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.4 : 1,
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
- }, children: [index > 0 && _jsx("span", { style: { margin: '0 5px', color: '#999' }, children: "\u2022" }), showMetadataNames && (_jsxs("span", { style: { color: '#666', marginRight: '5px' }, children: [md?.name || key, ":"] })), md?.dataDomain === MetadataDataDomains.DataList ? (_jsx(TMDataListItemViewer, { dataListId: md.dataListID, viewMode: md.dataListViewMode, value: value })) : md?.dataDomain === MetadataDataDomains.UserID ? (_jsx(TMDataUserIdItemViewer, { userId: value, showIcon: true })) : (_jsx("span", { style: {
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 === inputDcmts?.[0].DID ? _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] }));
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);