@topconsultnpm/sdkui-react 6.20.0-dev2.5 → 6.20.0-dev2.50
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/base/Styled.d.ts +1 -0
- package/lib/components/base/Styled.js +10 -2
- package/lib/components/base/TMTreeView.d.ts +3 -1
- package/lib/components/base/TMTreeView.js +63 -20
- 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 +1 -0
- package/lib/components/choosers/TMDataListItemPicker.js +178 -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/editors/TMLocalizedTextBox.d.ts +1 -0
- package/lib/components/editors/TMLocalizedTextBox.js +3 -3
- package/lib/components/editors/TMMetadataValues.js +3 -1
- package/lib/components/editors/TMTextBox.js +8 -9
- package/lib/components/features/archive/TMArchive.js +29 -42
- package/lib/components/features/documents/TMDcmtForm.js +165 -42
- package/lib/components/features/documents/TMDcmtPreview.js +2 -1
- package/lib/components/features/documents/TMMasterDetailDcmts.js +67 -6
- package/lib/components/features/documents/TMRelationViewer.d.ts +7 -1
- package/lib/components/features/documents/TMRelationViewer.js +389 -76
- package/lib/components/features/search/TMSearchResult.d.ts +1 -0
- package/lib/components/features/search/TMSearchResult.js +44 -82
- package/lib/components/features/search/TMSearchResultsMenuItems.js +2 -2
- package/lib/components/features/tasks/TMTaskForm.js +35 -187
- package/lib/components/features/tasks/TMTaskFormUtils.d.ts +74 -0
- package/lib/components/features/tasks/TMTaskFormUtils.js +538 -0
- package/lib/components/features/tasks/TMTasksUtils.d.ts +2 -0
- package/lib/components/features/tasks/TMTasksUtils.js +38 -7
- 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 +2 -2
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +2 -1
- package/lib/components/features/workflow/TMWorkflowPopup.js +2 -1
- package/lib/components/features/workflow/diagram/DiagramItemForm.js +1 -1
- package/lib/components/forms/Login/TMLoginForm.js +1 -1
- package/lib/components/forms/TMSaveForm.js +61 -13
- package/lib/components/grids/TMBlogsPost.js +2 -2
- 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 +2 -2
- package/lib/helper/Enum_Localizator.js +5 -0
- package/lib/helper/GlobalStyles.js +3 -0
- package/lib/helper/SDKUI_Globals.d.ts +4 -0
- package/lib/helper/SDKUI_Globals.js +6 -0
- package/lib/helper/SDKUI_Localizator.d.ts +11 -2
- package/lib/helper/SDKUI_Localizator.js +102 -12
- package/lib/helper/TMUtils.d.ts +18 -0
- package/lib/helper/TMUtils.js +58 -0
- package/lib/helper/helpers.d.ts +6 -2
- package/lib/helper/helpers.js +23 -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/useDcmtOperations.js +14 -2
- package/lib/hooks/useRelatedDocuments.js +34 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +11 -11
|
@@ -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([]);
|
|
@@ -156,6 +156,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
156
156
|
const lastLoadedInputRef = React.useRef('');
|
|
157
157
|
// Ref to track if user has manually expanded/collapsed static items
|
|
158
158
|
const userInteractedWithStaticItemsRef = React.useRef(false);
|
|
159
|
+
// Ref to track if we've already set the initial focused item
|
|
160
|
+
const initialFocusSetRef = React.useRef(false);
|
|
159
161
|
/**
|
|
160
162
|
* Generate a stable key from inputDcmts to detect real changes
|
|
161
163
|
*/
|
|
@@ -219,20 +221,27 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
219
221
|
const source = await searchResultToDataSource(searchResult);
|
|
220
222
|
if (source && source.length > 0) {
|
|
221
223
|
const dcmtDetails = [];
|
|
224
|
+
// Check once if this document type can have detail relations
|
|
225
|
+
// (optimization: avoid checking for each document)
|
|
226
|
+
const canHaveDetails = await hasDetailRelations(searchResult.fromTID);
|
|
222
227
|
for (const row of source) {
|
|
223
228
|
const rowGUID = genUniqueId();
|
|
224
229
|
const tid = row?.TID?.value;
|
|
225
230
|
const did = row?.DID?.value;
|
|
231
|
+
const isLogDel = row?.ISLOGDEL?.value;
|
|
226
232
|
dcmtDetails.push({
|
|
227
233
|
tid: tid,
|
|
228
234
|
did: did,
|
|
235
|
+
isLogDel: isLogDel,
|
|
229
236
|
key: `${tid}_${did}_${searchResult.relationID}_${mTID}_${mDID}_${rowGUID}`,
|
|
230
237
|
isDcmt: true,
|
|
231
238
|
isContainer: false,
|
|
232
239
|
isZero: false, // Documents are never zero (they exist)
|
|
240
|
+
isExpandible: canHaveDetails, // Can this doc be expanded?
|
|
233
241
|
values: row,
|
|
234
242
|
searchResult: [searchResult],
|
|
235
|
-
itemsCount
|
|
243
|
+
// Leave items and itemsCount undefined so TMTreeView shows expand arrow based on isExpandible
|
|
244
|
+
// Children will be loaded lazily by calculateItemsForNode when expanded
|
|
236
245
|
expanded: false,
|
|
237
246
|
hidden: false,
|
|
238
247
|
name: row?.SYS_Abstract?.value || row?.SYS_SUBJECT?.value || `Documento ${did}`
|
|
@@ -293,20 +302,30 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
293
302
|
const source = await searchResultToDataSource(searchResult);
|
|
294
303
|
if (source && source.length > 0) {
|
|
295
304
|
const dcmtMasters = [];
|
|
305
|
+
// Check once if this document type can have master relations (for inverted mode)
|
|
306
|
+
// or detail relations (for standard mode when expanding masters)
|
|
307
|
+
// (optimization: avoid checking for each document)
|
|
308
|
+
const canExpand = isForMaster && invertMasterNavigation
|
|
309
|
+
? await hasMasterRelations(searchResult.fromTID)
|
|
310
|
+
: await hasDetailRelations(searchResult.fromTID);
|
|
296
311
|
for (const row of source) {
|
|
297
312
|
const rowGUID = genUniqueId();
|
|
298
313
|
const tid = row?.TID?.value;
|
|
299
314
|
const did = row?.DID?.value;
|
|
315
|
+
const isLogDel = row?.ISLOGDEL?.value;
|
|
300
316
|
dcmtMasters.push({
|
|
301
317
|
tid: tid,
|
|
302
318
|
did: did,
|
|
319
|
+
isLogDel: isLogDel,
|
|
303
320
|
key: `${tid}_${did}_${searchResult.relationID}_${dTID}_${dDID}_${rowGUID}`,
|
|
304
321
|
isDcmt: true,
|
|
305
322
|
isContainer: false,
|
|
306
323
|
isZero: false, // Documents are never zero (they exist)
|
|
324
|
+
isExpandible: canExpand, // Can this doc be expanded?
|
|
307
325
|
values: row,
|
|
308
326
|
searchResult: [searchResult],
|
|
309
|
-
itemsCount
|
|
327
|
+
// Leave items and itemsCount undefined so TMTreeView shows expand arrow based on isExpandible
|
|
328
|
+
// Children will be loaded lazily by calculateItemsForNode when expanded
|
|
310
329
|
expanded: false,
|
|
311
330
|
hidden: false,
|
|
312
331
|
name: row?.SYS_Abstract?.value || row?.SYS_SUBJECT?.value || `Documento ${did}`
|
|
@@ -324,17 +343,128 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
324
343
|
return items;
|
|
325
344
|
}, [allowedTIDs]);
|
|
326
345
|
/**
|
|
327
|
-
* Update hidden property based on showZeroDcmts
|
|
346
|
+
* Update hidden property based on showZeroDcmts
|
|
347
|
+
* Updates items in-place without resetting isLoaded (no API calls needed)
|
|
328
348
|
*/
|
|
329
349
|
const updateHiddenProperty = useCallback((nodes) => {
|
|
350
|
+
// Safety check: if nodes is undefined or not an array, return empty array
|
|
351
|
+
if (!nodes || !Array.isArray(nodes)) {
|
|
352
|
+
return [];
|
|
353
|
+
}
|
|
330
354
|
return nodes.map(node => {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
355
|
+
let shouldHide = false;
|
|
356
|
+
let updatedItems = undefined;
|
|
357
|
+
if (node.items && Array.isArray(node.items)) {
|
|
358
|
+
// Recursively update children first - this creates a new array
|
|
359
|
+
let updatedChildren = updateHiddenProperty(node.items);
|
|
360
|
+
// Remove any existing auto-generated info messages (with key starting with __info__)
|
|
361
|
+
const filteredChildren = updatedChildren.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
362
|
+
// Check if all real children (excluding info messages) are hidden
|
|
363
|
+
const realChildren = filteredChildren.filter(child => !child.isInfoMessage);
|
|
364
|
+
const allChildrenHidden = realChildren.length > 0 && realChildren.every(child => child.hidden);
|
|
365
|
+
// If node is expanded AND showZeroDcmts is false AND all children are hidden, add info message
|
|
366
|
+
// This applies to both containers and document nodes with children
|
|
367
|
+
if (node.expanded && !showZeroDcmts && allChildrenHidden) {
|
|
368
|
+
// Add info message at the beginning, keep original children (hidden but preserved)
|
|
369
|
+
updatedItems = [
|
|
370
|
+
{
|
|
371
|
+
key: `__info__${node.key}`,
|
|
372
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
373
|
+
isContainer: false,
|
|
374
|
+
isDcmt: false,
|
|
375
|
+
isInfoMessage: true,
|
|
376
|
+
isExpandible: false
|
|
377
|
+
},
|
|
378
|
+
...filteredChildren
|
|
379
|
+
];
|
|
380
|
+
// Don't hide the node itself if it's showing an info message
|
|
381
|
+
shouldHide = false;
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
updatedItems = filteredChildren;
|
|
385
|
+
// Hide zero-item containers when showZeroDcmts is false (only if not showing info message)
|
|
386
|
+
shouldHide = !showZeroDcmts && (node.isZero ?? false);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// No items, apply normal hide logic
|
|
391
|
+
shouldHide = !showZeroDcmts && (node.isZero ?? false);
|
|
334
392
|
}
|
|
335
|
-
|
|
393
|
+
// IMPORTANT: Always create new object to trigger React re-render
|
|
394
|
+
return { ...node, hidden: shouldHide, items: updatedItems };
|
|
336
395
|
});
|
|
337
396
|
}, [showZeroDcmts]);
|
|
397
|
+
/**
|
|
398
|
+
* Helper function to set up initial tree expansion state.
|
|
399
|
+
* Called after all data is loaded but before setTreeData.
|
|
400
|
+
* Ensures: root container expanded, first document expanded with isRoot=true (for focus), first correlation folder expanded
|
|
401
|
+
* Returns a NEW tree with the modifications (immutable approach)
|
|
402
|
+
*/
|
|
403
|
+
const setupInitialTreeExpansion = (tree) => {
|
|
404
|
+
if (tree.length === 0)
|
|
405
|
+
return tree;
|
|
406
|
+
// ALWAYS expand the first container (even if empty, to show infoMessage)
|
|
407
|
+
const firstRootContainer = tree[0];
|
|
408
|
+
if (!firstRootContainer)
|
|
409
|
+
return tree;
|
|
410
|
+
// Create a deep copy of the first container with expanded=true, isRoot=true
|
|
411
|
+
let newFirstContainer = {
|
|
412
|
+
...firstRootContainer,
|
|
413
|
+
expanded: true,
|
|
414
|
+
isRoot: true
|
|
415
|
+
};
|
|
416
|
+
// 2. Find first document/container and expand it
|
|
417
|
+
const firstRootItems = newFirstContainer.items;
|
|
418
|
+
if (firstRootItems && firstRootItems.length > 0) {
|
|
419
|
+
const firstDocOrContainer = firstRootItems[0];
|
|
420
|
+
if (firstDocOrContainer.isDcmt) {
|
|
421
|
+
// First item is a document - expand it and mark as root for focus
|
|
422
|
+
let newFirstDoc = {
|
|
423
|
+
...firstDocOrContainer,
|
|
424
|
+
expanded: true,
|
|
425
|
+
isRoot: true
|
|
426
|
+
};
|
|
427
|
+
// 3. Expand first correlation folder (child of the document)
|
|
428
|
+
const docItems = newFirstDoc.items;
|
|
429
|
+
if (docItems && docItems.length > 0 && docItems[0].isContainer) {
|
|
430
|
+
const newFirstCorrelation = { ...docItems[0], expanded: true };
|
|
431
|
+
newFirstDoc = {
|
|
432
|
+
...newFirstDoc,
|
|
433
|
+
items: [newFirstCorrelation, ...docItems.slice(1)]
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
// Update the container's items
|
|
437
|
+
newFirstContainer = {
|
|
438
|
+
...newFirstContainer,
|
|
439
|
+
items: [newFirstDoc, ...firstRootItems.slice(1)]
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
else if (firstDocOrContainer.isContainer) {
|
|
443
|
+
// First item is a container (correlation folder) - expand it
|
|
444
|
+
let newFirstCorrelation = {
|
|
445
|
+
...firstDocOrContainer,
|
|
446
|
+
expanded: true
|
|
447
|
+
};
|
|
448
|
+
// Find first document inside this container and mark for focus
|
|
449
|
+
const containerItems = newFirstCorrelation.items;
|
|
450
|
+
if (containerItems && containerItems.length > 0 && containerItems[0].isDcmt) {
|
|
451
|
+
const newFirstDoc = { ...containerItems[0], isRoot: true };
|
|
452
|
+
newFirstCorrelation = {
|
|
453
|
+
...newFirstCorrelation,
|
|
454
|
+
items: [newFirstDoc, ...containerItems.slice(1)]
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
// Update the container's items
|
|
458
|
+
newFirstContainer = {
|
|
459
|
+
...newFirstContainer,
|
|
460
|
+
items: [newFirstCorrelation, ...firstRootItems.slice(1)]
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// If firstRootItems is empty/undefined, the container is still expanded (will show infoMessage)
|
|
465
|
+
// Return new tree with the modified first container
|
|
466
|
+
return [newFirstContainer, ...tree.slice(1)];
|
|
467
|
+
};
|
|
338
468
|
/**
|
|
339
469
|
* Main data loading function
|
|
340
470
|
*/
|
|
@@ -405,13 +535,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
405
535
|
tid: dcmt.TID,
|
|
406
536
|
dtd,
|
|
407
537
|
isContainer: true,
|
|
408
|
-
isRoot: true,
|
|
409
538
|
isLoaded: true,
|
|
410
539
|
isZero: false,
|
|
411
540
|
searchResult: result ? [result] : [],
|
|
412
541
|
items: relatedDocs, // Directly show detail containers
|
|
413
542
|
itemsCount: relatedDocs.length,
|
|
414
|
-
expanded:
|
|
543
|
+
expanded: false,
|
|
415
544
|
hidden: false
|
|
416
545
|
};
|
|
417
546
|
tree.push(typeContainer);
|
|
@@ -426,10 +555,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
426
555
|
did: dcmt.DID,
|
|
427
556
|
isDcmt: true,
|
|
428
557
|
isContainer: false,
|
|
429
|
-
expanded:
|
|
558
|
+
expanded: false,
|
|
430
559
|
isZero: dcmt.DID === 0,
|
|
431
560
|
isMaster: !isForMaster,
|
|
432
|
-
isRoot: true,
|
|
433
561
|
isLoaded: true,
|
|
434
562
|
hidden: false,
|
|
435
563
|
values: docRow,
|
|
@@ -444,13 +572,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
444
572
|
tid: dcmt.TID,
|
|
445
573
|
dtd,
|
|
446
574
|
isContainer: true,
|
|
447
|
-
isRoot: true,
|
|
448
575
|
isLoaded: true,
|
|
449
576
|
isZero: false, // Type container is never zero (contains documents)
|
|
450
577
|
searchResult: result ? [result] : [],
|
|
451
578
|
items: [docNode],
|
|
452
579
|
itemsCount: 1,
|
|
453
|
-
expanded:
|
|
580
|
+
expanded: false,
|
|
454
581
|
hidden: false
|
|
455
582
|
};
|
|
456
583
|
tree.push(typeContainer);
|
|
@@ -460,8 +587,55 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
460
587
|
processedCount++;
|
|
461
588
|
setWaitPanelValuePrimary(processedCount);
|
|
462
589
|
}
|
|
463
|
-
|
|
464
|
-
|
|
590
|
+
/**
|
|
591
|
+
* Helper function to check if a node has any actual correlated documents
|
|
592
|
+
* Returns true if there are NO documents (relations are empty)
|
|
593
|
+
*/
|
|
594
|
+
const hasNoActualDocuments = (items) => {
|
|
595
|
+
if (!items || items.length === 0)
|
|
596
|
+
return true;
|
|
597
|
+
// Check each item
|
|
598
|
+
return items.every(item => {
|
|
599
|
+
// If it's a container, check if it's zero or has no documents
|
|
600
|
+
if (item.isContainer) {
|
|
601
|
+
// Container with isZero=true has no documents
|
|
602
|
+
if (item.isZero)
|
|
603
|
+
return true;
|
|
604
|
+
// Container with no items has no documents
|
|
605
|
+
if (!item.items || item.items.length === 0)
|
|
606
|
+
return true;
|
|
607
|
+
// Otherwise, it has documents
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
// If it's a document node, check its children (relation containers)
|
|
611
|
+
if (item.isDcmt) {
|
|
612
|
+
// Check if all child containers are empty
|
|
613
|
+
return hasNoActualDocuments(item.items);
|
|
614
|
+
}
|
|
615
|
+
// For other types, assume no documents
|
|
616
|
+
return true;
|
|
617
|
+
});
|
|
618
|
+
};
|
|
619
|
+
// Check if there are no relations for any document
|
|
620
|
+
const hasNoRelations = tree.length === 0 || tree.every(container => {
|
|
621
|
+
// Check if container has no items
|
|
622
|
+
if (!container.items || container.items.length === 0)
|
|
623
|
+
return true;
|
|
624
|
+
// Check recursively if there are any actual documents
|
|
625
|
+
return hasNoActualDocuments(container.items);
|
|
626
|
+
});
|
|
627
|
+
// If no relations found, notify parent
|
|
628
|
+
if (hasNoRelations && onNoRelationsFound) {
|
|
629
|
+
onNoRelationsFound();
|
|
630
|
+
}
|
|
631
|
+
// FIRST setup initial expansion state (root container expanded, first document expanded with focus, first correlation folder expanded)
|
|
632
|
+
// This must run BEFORE updateHiddenProperty so that infoMessage logic sees expanded=true
|
|
633
|
+
const expandedTree = setupInitialTreeExpansion(tree);
|
|
634
|
+
// THEN apply hidden property transformations (creates new objects, adds infoMessage where needed)
|
|
635
|
+
// Now it will correctly detect expanded nodes and add infoMessage if all children are hidden
|
|
636
|
+
const processedTree = updateHiddenProperty(expandedTree);
|
|
637
|
+
setTreeData(processedTree);
|
|
638
|
+
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty, showMainDocument, labelMainContainer, onNoRelationsFound]);
|
|
465
639
|
/**
|
|
466
640
|
* Merge main tree data with additional static items
|
|
467
641
|
*/
|
|
@@ -509,10 +683,56 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
509
683
|
}, [inputDcmts, dcmtTypes, maxDepthLevel, getInputKey, loadData, treeData.length]);
|
|
510
684
|
/**
|
|
511
685
|
* Update tree when showZeroDcmts changes
|
|
686
|
+
* Updates the visualization without re-fetching data (isLoaded stays true)
|
|
512
687
|
*/
|
|
513
688
|
useEffect(() => {
|
|
514
689
|
setTreeData(prevData => updateHiddenProperty(prevData));
|
|
515
690
|
}, [showZeroDcmts, updateHiddenProperty]);
|
|
691
|
+
/**
|
|
692
|
+
* Set initial focused item when showMainDocument = true
|
|
693
|
+
* Focuses on the main document (first inputDcmts) when data is loaded
|
|
694
|
+
* For master mode with invertMasterNavigation=false, focuses on the first master document
|
|
695
|
+
*/
|
|
696
|
+
useEffect(() => {
|
|
697
|
+
// Only execute if:
|
|
698
|
+
// 1. showMainDocument is true
|
|
699
|
+
// 2. onFocusedItemChanged callback exists
|
|
700
|
+
// 3. We have tree data
|
|
701
|
+
// 4. We have input documents
|
|
702
|
+
// 5. We haven't already set the initial focus
|
|
703
|
+
if (!showMainDocument || !onFocusedItemChanged || !treeData.length || !inputDcmts?.length || initialFocusSetRef.current) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
// Helper function to recursively find the first document with isRoot=true
|
|
707
|
+
const findFirstRootDocument = (items) => {
|
|
708
|
+
for (const item of items) {
|
|
709
|
+
// Check if this item is a document with isRoot=true
|
|
710
|
+
if (item.isDcmt && item.isRoot) {
|
|
711
|
+
return item;
|
|
712
|
+
}
|
|
713
|
+
// Recursively search in children
|
|
714
|
+
if (item.items && Array.isArray(item.items)) {
|
|
715
|
+
const found = findFirstRootDocument(item.items);
|
|
716
|
+
if (found)
|
|
717
|
+
return found;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return null;
|
|
721
|
+
};
|
|
722
|
+
// Find the first document marked as root (set by setupInitialTreeExpansion)
|
|
723
|
+
const docNode = findFirstRootDocument(treeData);
|
|
724
|
+
if (docNode) {
|
|
725
|
+
// Set the focused item
|
|
726
|
+
onFocusedItemChanged(docNode);
|
|
727
|
+
initialFocusSetRef.current = true;
|
|
728
|
+
}
|
|
729
|
+
}, [treeData, showMainDocument, onFocusedItemChanged, inputDcmts]);
|
|
730
|
+
/**
|
|
731
|
+
* Reset initial focus flag when input documents change
|
|
732
|
+
*/
|
|
733
|
+
useEffect(() => {
|
|
734
|
+
initialFocusSetRef.current = false;
|
|
735
|
+
}, [getInputKey()]);
|
|
516
736
|
/**
|
|
517
737
|
* Sync static items state when additionalStaticItems change
|
|
518
738
|
* IMPORTANT: Only update if user hasn't manually interacted with the tree,
|
|
@@ -548,6 +768,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
548
768
|
/**
|
|
549
769
|
* Calculate items for node when expanded (lazy loading)
|
|
550
770
|
* Note: additionalStaticItems are already fully loaded, so skip calculation for them
|
|
771
|
+
*
|
|
772
|
+
* PERFORMANCE OPTIMIZATION:
|
|
773
|
+
* - Containers: Return items immediately (no API calls) - items are already loaded
|
|
774
|
+
* - Documents: Load children lazily only when the specific document is expanded (1 API call)
|
|
551
775
|
*/
|
|
552
776
|
const calculateItemsForNode = useCallback(async (node) => {
|
|
553
777
|
// Skip calculation for separator (it has no children)
|
|
@@ -558,62 +782,129 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
558
782
|
if (node.isStaticItem || node.isAdditionalContainer || node.isAdditional) {
|
|
559
783
|
return node.items;
|
|
560
784
|
}
|
|
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;
|
|
785
|
+
// If already loaded, apply current visualization logic without re-fetching
|
|
786
|
+
if (node.isLoaded && node.items) {
|
|
787
|
+
// Apply current show/hide logic based on showZeroDcmts
|
|
788
|
+
const updatedItems = updateHiddenProperty(node.items);
|
|
789
|
+
// If this is a container, check if we need to show info message
|
|
790
|
+
if (node.isContainer) {
|
|
791
|
+
// Remove any existing info messages
|
|
792
|
+
const filteredItems = updatedItems.filter(child => !(child.isInfoMessage && child.key?.startsWith('__info__')));
|
|
793
|
+
// Check if all real children are hidden
|
|
794
|
+
const realChildren = filteredItems.filter(child => !child.isInfoMessage);
|
|
795
|
+
const allChildrenHidden = realChildren.length > 0 && realChildren.every(child => child.hidden);
|
|
796
|
+
// If showZeroDcmts is false and all items are hidden, show info message + keep hidden containers
|
|
797
|
+
if (!showZeroDcmts && allChildrenHidden) {
|
|
798
|
+
return [
|
|
799
|
+
{
|
|
800
|
+
key: `__info__${node.key}`,
|
|
801
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
802
|
+
isContainer: false,
|
|
803
|
+
isDcmt: false,
|
|
804
|
+
isInfoMessage: true,
|
|
805
|
+
isExpandible: false
|
|
806
|
+
},
|
|
807
|
+
...filteredItems // Keep hidden containers so they can be shown when toggling
|
|
808
|
+
];
|
|
603
809
|
}
|
|
604
|
-
newItems.push(item);
|
|
605
810
|
}
|
|
606
|
-
return
|
|
811
|
+
return updatedItems;
|
|
607
812
|
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
813
|
+
// ============================================
|
|
814
|
+
// CONTAINER: Show items immediately (no API calls)
|
|
815
|
+
// Items are already loaded from initial load or parent expansion
|
|
816
|
+
// ============================================
|
|
817
|
+
if (node.isContainer) {
|
|
818
|
+
// No API calls needed - just return existing items
|
|
819
|
+
// Children of these items will be loaded lazily when user expands each document
|
|
820
|
+
// Apply updateHiddenProperty to respect current showZeroDcmts setting
|
|
821
|
+
// If node has no items, return empty array
|
|
822
|
+
if (!node.items)
|
|
823
|
+
return [];
|
|
824
|
+
const updatedItems = updateHiddenProperty(node.items);
|
|
825
|
+
// If showZeroDcmts is false and all items are hidden, add info message + keep hidden containers
|
|
826
|
+
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
827
|
+
return [
|
|
828
|
+
{
|
|
829
|
+
key: `__info__${node.key}`,
|
|
830
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
831
|
+
isContainer: false,
|
|
832
|
+
isDcmt: false,
|
|
833
|
+
isInfoMessage: true,
|
|
834
|
+
isExpandible: false
|
|
835
|
+
},
|
|
836
|
+
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
837
|
+
];
|
|
838
|
+
}
|
|
839
|
+
return updatedItems;
|
|
611
840
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
841
|
+
// ============================================
|
|
842
|
+
// DOCUMENT: Load its children (relation containers) lazily
|
|
843
|
+
// Only makes ONE API call for this specific document
|
|
844
|
+
// ============================================
|
|
845
|
+
if (node.isDcmt && node.tid && node.did) {
|
|
846
|
+
const newAbortController = new AbortController();
|
|
847
|
+
setExpansionAbortController(newAbortController);
|
|
848
|
+
setShowExpansionWaitPanel(true);
|
|
849
|
+
setExpansionWaitPanelMaxValue(1);
|
|
850
|
+
setExpansionWaitPanelValue(0);
|
|
851
|
+
setExpansionWaitPanelText(`Caricamento documenti correlati...`);
|
|
852
|
+
try {
|
|
853
|
+
// Check for abort
|
|
854
|
+
if (newAbortController.signal.aborted) {
|
|
855
|
+
return [];
|
|
856
|
+
}
|
|
857
|
+
// Determine which load function to use based on mode
|
|
858
|
+
let loadedItems = [];
|
|
859
|
+
if (isForMaster && !invertMasterNavigation) {
|
|
860
|
+
// Original mode: detail documents load detail documents (natural detail→detail navigation)
|
|
861
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
862
|
+
}
|
|
863
|
+
else if (isForMaster) {
|
|
864
|
+
// Inverted master mode: load master documents
|
|
865
|
+
loadedItems = await getMasterDcmtsAsync(node.tid, node.did, 1);
|
|
866
|
+
}
|
|
867
|
+
else {
|
|
868
|
+
// Standard mode: load detail documents
|
|
869
|
+
loadedItems = await getDetailDcmtsAsync(node.tid, node.did, 1);
|
|
870
|
+
}
|
|
871
|
+
// Espandi automaticamente il primo container se ci sono documenti correlati
|
|
872
|
+
if (loadedItems.length > 0 && loadedItems[0].isContainer) {
|
|
873
|
+
loadedItems[0].expanded = true;
|
|
874
|
+
}
|
|
875
|
+
// Apply updateHiddenProperty to respect current showZeroDcmts setting
|
|
876
|
+
// This ensures that dynamically loaded nodes respect the visibility rules:
|
|
877
|
+
// - Relation type containers are always visible (even if empty)
|
|
878
|
+
// - Other containers with zero items can be hidden based on showZeroDcmts
|
|
879
|
+
const updatedItems = updateHiddenProperty(loadedItems);
|
|
880
|
+
// If showZeroDcmts is false and all items are hidden, add info message + keep hidden containers
|
|
881
|
+
if (!showZeroDcmts && updatedItems.length > 0 && updatedItems.every(item => item.hidden)) {
|
|
882
|
+
return [
|
|
883
|
+
{
|
|
884
|
+
key: `__info__${node.key}`,
|
|
885
|
+
name: 'Nessun documento correlato da visualizzare',
|
|
886
|
+
isContainer: false,
|
|
887
|
+
isDcmt: false,
|
|
888
|
+
isInfoMessage: true,
|
|
889
|
+
isExpandible: false
|
|
890
|
+
},
|
|
891
|
+
...updatedItems // Keep hidden containers so they can be shown when toggling
|
|
892
|
+
];
|
|
893
|
+
}
|
|
894
|
+
return updatedItems;
|
|
895
|
+
}
|
|
896
|
+
catch (error) {
|
|
897
|
+
console.error('Error loading document relations:', error);
|
|
898
|
+
return [];
|
|
899
|
+
}
|
|
900
|
+
finally {
|
|
901
|
+
setShowExpansionWaitPanel(false);
|
|
902
|
+
setExpansionAbortController(undefined);
|
|
903
|
+
}
|
|
615
904
|
}
|
|
616
|
-
|
|
905
|
+
// Default: return existing items
|
|
906
|
+
return node.items;
|
|
907
|
+
}, [isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, showZeroDcmts]);
|
|
617
908
|
/**
|
|
618
909
|
* Default item renderer with metadata display (adapted from TMMasterDetailDcmts.tsx)
|
|
619
910
|
*/
|
|
@@ -626,6 +917,18 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
626
917
|
e.stopPropagation();
|
|
627
918
|
onDocumentDoubleClick?.(item.tid, item.did, item.name);
|
|
628
919
|
};
|
|
920
|
+
// Info message rendering
|
|
921
|
+
if (item.isInfoMessage) {
|
|
922
|
+
return (_jsxs("div", { style: {
|
|
923
|
+
display: 'flex',
|
|
924
|
+
alignItems: 'center',
|
|
925
|
+
gap: '10px',
|
|
926
|
+
height: '32px',
|
|
927
|
+
padding: '6px 0',
|
|
928
|
+
color: '#666',
|
|
929
|
+
fontStyle: 'italic'
|
|
930
|
+
}, children: [_jsx(IconCircleInfo, { fontSize: 20, color: TMColors.iconLight }), _jsx("span", { children: item.name })] }));
|
|
931
|
+
}
|
|
629
932
|
// Container rendering
|
|
630
933
|
if (item.isContainer || !item.isDcmt) {
|
|
631
934
|
const defaultContainerStyle = {
|
|
@@ -634,7 +937,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
634
937
|
gap: '10px',
|
|
635
938
|
height: '32px',
|
|
636
939
|
padding: '6px 0',
|
|
637
|
-
opacity: item.isZero ? 0.
|
|
940
|
+
opacity: item.isZero ? 0.99 : 1,
|
|
638
941
|
transition: 'opacity 0.2s ease-in-out'
|
|
639
942
|
};
|
|
640
943
|
// Se è il container principale (root) e showMainDocument è false,
|
|
@@ -653,6 +956,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
653
956
|
return (_jsx("div", { style: defaultContainerStyle, children: content }));
|
|
654
957
|
}
|
|
655
958
|
// Document rendering with full metadata display
|
|
959
|
+
const isLogicallyDeleted = Number(item.isLogDel) === 1;
|
|
656
960
|
const defaultDocumentStyle = {
|
|
657
961
|
minWidth: '90px',
|
|
658
962
|
width: '100%',
|
|
@@ -664,18 +968,23 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
664
968
|
alignItems: 'center',
|
|
665
969
|
cursor: 'pointer',
|
|
666
970
|
userSelect: 'none',
|
|
667
|
-
opacity: item.isZero ? 0.
|
|
668
|
-
transition: 'opacity 0.2s ease-in-out'
|
|
971
|
+
opacity: item.isZero ? 0.99 : 1,
|
|
972
|
+
transition: 'opacity 0.2s ease-in-out',
|
|
973
|
+
textDecoration: isLogicallyDeleted ? 'line-through' : 'none'
|
|
669
974
|
};
|
|
670
975
|
const documentStyle = customDocumentStyle
|
|
671
976
|
? { ...defaultDocumentStyle, ...customDocumentStyle(item) }
|
|
672
977
|
: defaultDocumentStyle;
|
|
978
|
+
const textDecoration = isLogicallyDeleted ? 'line-through' : 'none';
|
|
979
|
+
const textColor = isLogicallyDeleted ? 'gray' : undefined;
|
|
673
980
|
const defaultMetadataContent = item.values && (_jsx(StyledDivHorizontal, { style: {
|
|
674
981
|
fontSize: '1rem',
|
|
675
982
|
overflow: 'hidden',
|
|
676
983
|
flex: 1,
|
|
677
984
|
minWidth: 0,
|
|
678
|
-
whiteSpace: 'nowrap'
|
|
985
|
+
whiteSpace: 'nowrap',
|
|
986
|
+
textDecoration: textDecoration,
|
|
987
|
+
color: textColor
|
|
679
988
|
}, children: getDcmtDisplayValue(item.values).map((key, index) => {
|
|
680
989
|
const md = item.values?.[key]?.md;
|
|
681
990
|
const value = item.values?.[key]?.value;
|
|
@@ -684,12 +993,16 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
684
993
|
return (_jsxs(StyledDivHorizontal, { style: {
|
|
685
994
|
flexShrink: isLast ? 1 : 0,
|
|
686
995
|
minWidth: isLast ? 0 : 'auto',
|
|
687
|
-
overflow: isLast ? 'hidden' : 'visible'
|
|
688
|
-
|
|
996
|
+
overflow: isLast ? 'hidden' : 'visible',
|
|
997
|
+
textDecoration: textDecoration,
|
|
998
|
+
color: textColor
|
|
999
|
+
}, 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
1000
|
fontWeight: 500,
|
|
690
1001
|
overflow: isLast ? 'hidden' : 'visible',
|
|
691
1002
|
textOverflow: isLast ? 'ellipsis' : 'clip',
|
|
692
|
-
whiteSpace: 'nowrap'
|
|
1003
|
+
whiteSpace: 'nowrap',
|
|
1004
|
+
textDecoration: textDecoration,
|
|
1005
|
+
color: textColor
|
|
693
1006
|
}, children: value }))] }, `${key}_${index}`));
|
|
694
1007
|
}) }));
|
|
695
1008
|
const metadataContent = customDocumentContent
|
|
@@ -744,7 +1057,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
744
1057
|
if (mergedTreeData.length === 0) {
|
|
745
1058
|
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
|
|
746
1059
|
}
|
|
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) => {
|
|
1060
|
+
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
1061
|
setTimeout(() => {
|
|
749
1062
|
abortController?.abort();
|
|
750
1063
|
}, 100);
|