@topconsultnpm/sdkui-react 6.19.0-dev1.20 → 6.19.0-dev1.22
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/features/documents/TMRelationViewer.d.ts +41 -3
- package/lib/components/features/documents/TMRelationViewer.js +157 -45
- package/lib/components/features/search/TMSearch.d.ts +1 -0
- package/lib/components/features/search/TMSearch.js +13 -4
- package/lib/components/features/search/TMSearchResult.js +16 -539
- package/lib/hooks/useRelatedDocuments.d.ts +72 -0
- package/lib/hooks/useRelatedDocuments.js +629 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { DcmtTypeDescriptor, SearchResultDescriptor } from "@topconsultnpm/sdk-ts";
|
|
2
|
+
import { DcmtTypeDescriptor, SearchResultDescriptor, DataColumnDescriptor } from "@topconsultnpm/sdk-ts";
|
|
3
3
|
import { DcmtInfo } from '../../../ts';
|
|
4
4
|
import { ITMTreeItem } from '../../base/TMTreeView';
|
|
5
5
|
/**
|
|
@@ -17,6 +17,7 @@ export interface RelationTreeItem extends ITMTreeItem {
|
|
|
17
17
|
isMaster?: boolean;
|
|
18
18
|
isCorrelated?: boolean;
|
|
19
19
|
isLoaded?: boolean;
|
|
20
|
+
isSeparator?: boolean;
|
|
20
21
|
values?: any;
|
|
21
22
|
searchResult?: SearchResultDescriptor[];
|
|
22
23
|
itemsCount?: number;
|
|
@@ -49,8 +50,22 @@ export interface TMRelationViewerProps {
|
|
|
49
50
|
onSelectedItemsChanged?: (items: RelationTreeItem[]) => void;
|
|
50
51
|
/** Callback when a document is double-clicked */
|
|
51
52
|
onDocumentDoubleClick?: (tid: number, did: number, name?: string) => void;
|
|
52
|
-
/** Custom item renderer */
|
|
53
|
-
customItemRender?: (item: RelationTreeItem | null) => JSX.Element;
|
|
53
|
+
/** Custom item renderer (full control). Return undefined to use default renderer. */
|
|
54
|
+
customItemRender?: (item: RelationTreeItem | null) => JSX.Element | undefined;
|
|
55
|
+
/** Custom container style function */
|
|
56
|
+
customContainerStyle?: (item: RelationTreeItem) => React.CSSProperties;
|
|
57
|
+
/** Custom document style function */
|
|
58
|
+
customDocumentStyle?: (item: RelationTreeItem) => React.CSSProperties;
|
|
59
|
+
/** Custom container content renderer (partial control - replaces only content, keeps structure) */
|
|
60
|
+
customContainerContent?: (item: RelationTreeItem, defaultContent: JSX.Element) => JSX.Element;
|
|
61
|
+
/** Custom document content renderer (partial control - replaces only metadata display) */
|
|
62
|
+
customDocumentContent?: (item: RelationTreeItem, defaultMetadataContent: JSX.Element) => JSX.Element;
|
|
63
|
+
/**
|
|
64
|
+
* Show metadata names before values (default: false).
|
|
65
|
+
* When true, displays "MetadataName: Value", otherwise just "Value".
|
|
66
|
+
* Value rendering respects DataDomain (uses TMDataListItemViewer for lists, TMUserIdViewer for users, etc.)
|
|
67
|
+
*/
|
|
68
|
+
showMetadataNames?: boolean;
|
|
54
69
|
/** Maximum depth level for recursive loading (default: 2) */
|
|
55
70
|
maxDepthLevel?: number;
|
|
56
71
|
/**
|
|
@@ -58,6 +73,13 @@ export interface TMRelationViewerProps {
|
|
|
58
73
|
* If false, when isForMaster=true shows: master docs → detail docs as children (original navigation)
|
|
59
74
|
*/
|
|
60
75
|
invertMasterNavigation?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Additional static items to append at the end of the tree.
|
|
78
|
+
* These items are not loaded dynamically - they must already contain all data.
|
|
79
|
+
* Useful for showing pre-calculated documents (e.g., additional documents in dossiers).
|
|
80
|
+
* Can include a separator item by marking it with `isSeparator: true`.
|
|
81
|
+
*/
|
|
82
|
+
additionalStaticItems?: RelationTreeItem[];
|
|
61
83
|
}
|
|
62
84
|
/**
|
|
63
85
|
* Check if document type has detail relations
|
|
@@ -67,5 +89,21 @@ export declare const hasDetailRelations: (mTID: number | undefined) => Promise<b
|
|
|
67
89
|
* Check if document type has master relations
|
|
68
90
|
*/
|
|
69
91
|
export declare const hasMasterRelations: (dTID: number | undefined) => Promise<boolean>;
|
|
92
|
+
/**
|
|
93
|
+
* Get metadata keys excluding system metadata
|
|
94
|
+
*/
|
|
95
|
+
export declare const getMetadataKeys: (obj: any) => string[];
|
|
96
|
+
/**
|
|
97
|
+
* Get display value keys for a document (max 5, prioritize SYS_Abstract)
|
|
98
|
+
*/
|
|
99
|
+
export declare const getDcmtDisplayValue: (obj: any) => string[];
|
|
100
|
+
/**
|
|
101
|
+
* Get display value formatted by column type
|
|
102
|
+
*/
|
|
103
|
+
export declare const getDisplayValueByColumn: (col: DataColumnDescriptor | undefined, value: any) => any;
|
|
104
|
+
/**
|
|
105
|
+
* Convert SearchResultDescriptor to structured data source with metadata
|
|
106
|
+
*/
|
|
107
|
+
export declare const searchResultToDataSource: (searchResult: SearchResultDescriptor | undefined, hideSysMetadata?: boolean) => Promise<any[]>;
|
|
70
108
|
declare const TMRelationViewer: React.FC<TMRelationViewerProps>;
|
|
71
109
|
export default TMRelationViewer;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useCallback, useEffect, useState } from 'react';
|
|
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,
|
|
4
|
+
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight } from '../../../helper';
|
|
5
5
|
import { TMColors } from '../../../utils/theme';
|
|
6
6
|
import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
|
|
7
7
|
import TMTreeView from '../../base/TMTreeView';
|
|
@@ -47,26 +47,32 @@ const isManyToManyRelation = async (relationID) => {
|
|
|
47
47
|
/**
|
|
48
48
|
* Get metadata keys excluding system metadata
|
|
49
49
|
*/
|
|
50
|
-
|
|
50
|
+
export const getMetadataKeys = (obj) => {
|
|
51
51
|
if (!obj)
|
|
52
52
|
return [];
|
|
53
53
|
const keys = Object.keys(obj);
|
|
54
|
+
// Escludi metadati di sistema (MID < 100 che sono uppercase) e altri campi tecnici
|
|
54
55
|
const sysMIDs = Object.values(SystemMIDs).map(o => o.toUpperCase());
|
|
55
56
|
return keys.filter(k => obj?.[k].value && !sysMIDs.includes(k)).filter(o => o !== "rowIndex" && o !== "ISLEXPROT");
|
|
56
|
-
}
|
|
57
|
+
};
|
|
57
58
|
/**
|
|
58
59
|
* Get display value keys for a document (max 5, prioritize SYS_Abstract)
|
|
59
60
|
*/
|
|
60
|
-
|
|
61
|
+
export const getDcmtDisplayValue = (obj) => {
|
|
62
|
+
if (!obj)
|
|
63
|
+
return [];
|
|
64
|
+
// Prima cerca SYS_Abstract
|
|
65
|
+
if (obj['SYS_Abstract']?.value) {
|
|
66
|
+
return ['SYS_Abstract'];
|
|
67
|
+
}
|
|
68
|
+
// Altrimenti prendi i primi 5 metadati non di sistema
|
|
61
69
|
const mdKeys = getMetadataKeys(obj);
|
|
62
|
-
if (mdKeys.includes("SYS_Abstract"))
|
|
63
|
-
return ["SYS_Abstract"];
|
|
64
70
|
return mdKeys.slice(0, 5);
|
|
65
|
-
}
|
|
71
|
+
};
|
|
66
72
|
/**
|
|
67
73
|
* Get display value formatted by column type
|
|
68
74
|
*/
|
|
69
|
-
const getDisplayValueByColumn = (col, value) => {
|
|
75
|
+
export const getDisplayValueByColumn = (col, value) => {
|
|
70
76
|
if (!value)
|
|
71
77
|
return value;
|
|
72
78
|
if (!col)
|
|
@@ -107,7 +113,7 @@ const getDisplayValueByColumn = (col, value) => {
|
|
|
107
113
|
/**
|
|
108
114
|
* Convert SearchResultDescriptor to structured data source with metadata
|
|
109
115
|
*/
|
|
110
|
-
const searchResultToDataSource = async (searchResult, hideSysMetadata) => {
|
|
116
|
+
export const searchResultToDataSource = async (searchResult, hideSysMetadata) => {
|
|
111
117
|
const rows = searchResult?.dtdResult?.rows ?? [];
|
|
112
118
|
const tid = searchResult?.fromTID;
|
|
113
119
|
const dtd = await DcmtTypeListCacheService.GetAsync(tid);
|
|
@@ -133,11 +139,12 @@ const searchResultToDataSource = async (searchResult, hideSysMetadata) => {
|
|
|
133
139
|
}
|
|
134
140
|
return output;
|
|
135
141
|
};
|
|
136
|
-
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onDocumentDoubleClick, customItemRender, maxDepthLevel = 2, invertMasterNavigation = true, }) => {
|
|
142
|
+
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onDocumentDoubleClick, customItemRender, customContainerStyle, customDocumentStyle, customContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems = [], }) => {
|
|
137
143
|
// State
|
|
138
144
|
const [dcmtTypes, setDcmtTypes] = useState([]);
|
|
139
145
|
const [treeData, setTreeData] = useState([]);
|
|
140
146
|
const [showZeroDcmts, setShowZeroDcmts] = useState(initialShowZeroDcmts);
|
|
147
|
+
const [staticItemsState, setStaticItemsState] = useState([]);
|
|
141
148
|
// Wait Panel State (only used if allowWaitPanel is true)
|
|
142
149
|
const [showWaitPanel, setShowWaitPanel] = useState(false);
|
|
143
150
|
const [waitPanelTextPrimary, setWaitPanelTextPrimary] = useState('');
|
|
@@ -146,6 +153,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
146
153
|
const [abortController] = useState(new AbortController());
|
|
147
154
|
// Ref to track last loaded input to prevent unnecessary reloads
|
|
148
155
|
const lastLoadedInputRef = React.useRef('');
|
|
156
|
+
// Ref to track if user has manually expanded/collapsed static items
|
|
157
|
+
const userInteractedWithStaticItemsRef = React.useRef(false);
|
|
149
158
|
/**
|
|
150
159
|
* Generate a stable key from inputDcmts to detect real changes
|
|
151
160
|
*/
|
|
@@ -428,6 +437,20 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
428
437
|
}
|
|
429
438
|
setTreeData(updateHiddenProperty(tree));
|
|
430
439
|
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, abortController, updateHiddenProperty]);
|
|
440
|
+
/**
|
|
441
|
+
* Merge main tree data with additional static items
|
|
442
|
+
*/
|
|
443
|
+
const mergedTreeData = useMemo(() => {
|
|
444
|
+
if (!additionalStaticItems || additionalStaticItems.length === 0) {
|
|
445
|
+
return treeData;
|
|
446
|
+
}
|
|
447
|
+
// Use staticItemsState if available (preserves expanded state), otherwise use fresh additionalStaticItems
|
|
448
|
+
const itemsToMerge = staticItemsState.length > 0 ? staticItemsState : additionalStaticItems;
|
|
449
|
+
// Append additional static items at the end
|
|
450
|
+
// NOTE: Do NOT call updateHiddenProperty on static items - they are pre-processed by the parent
|
|
451
|
+
// and may contain custom properties that would be lost by the spread operator
|
|
452
|
+
return [...treeData, ...itemsToMerge];
|
|
453
|
+
}, [treeData, additionalStaticItems, staticItemsState]);
|
|
431
454
|
/**
|
|
432
455
|
* Load data when inputs change
|
|
433
456
|
*/
|
|
@@ -435,6 +458,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
435
458
|
if (!inputDcmts || inputDcmts.length === 0 || dcmtTypes.length === 0) {
|
|
436
459
|
setTreeData([]);
|
|
437
460
|
lastLoadedInputRef.current = '';
|
|
461
|
+
userInteractedWithStaticItemsRef.current = false; // Reset interaction flag
|
|
438
462
|
return;
|
|
439
463
|
}
|
|
440
464
|
// Generate current input key
|
|
@@ -445,6 +469,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
445
469
|
}
|
|
446
470
|
// Mark as loading this key
|
|
447
471
|
lastLoadedInputRef.current = currentKey;
|
|
472
|
+
// Reset interaction flag when loading new data
|
|
473
|
+
userInteractedWithStaticItemsRef.current = false;
|
|
448
474
|
setShowWaitPanel(true);
|
|
449
475
|
setWaitPanelMaxValuePrimary(inputDcmts.length);
|
|
450
476
|
setWaitPanelValuePrimary(0);
|
|
@@ -462,10 +488,51 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
462
488
|
useEffect(() => {
|
|
463
489
|
setTreeData(prevData => updateHiddenProperty(prevData));
|
|
464
490
|
}, [showZeroDcmts, updateHiddenProperty]);
|
|
491
|
+
/**
|
|
492
|
+
* Sync static items state when additionalStaticItems change
|
|
493
|
+
* IMPORTANT: Only update if user hasn't manually interacted with the tree,
|
|
494
|
+
* to preserve expand/collapse state
|
|
495
|
+
*/
|
|
496
|
+
useEffect(() => {
|
|
497
|
+
// If user hasn't interacted yet, sync with incoming additionalStaticItems
|
|
498
|
+
if (!userInteractedWithStaticItemsRef.current) {
|
|
499
|
+
setStaticItemsState(additionalStaticItems || []);
|
|
500
|
+
}
|
|
501
|
+
// If user has interacted, we need to merge the new data with existing state
|
|
502
|
+
// to preserve expanded/collapsed state while updating content
|
|
503
|
+
else if (additionalStaticItems && additionalStaticItems.length > 0) {
|
|
504
|
+
setStaticItemsState(prevState => {
|
|
505
|
+
// Create a map of existing state by key
|
|
506
|
+
const stateMap = new Map(prevState.map(item => [item.key, item]));
|
|
507
|
+
// Merge: use existing state if available (preserves expanded), otherwise use new item
|
|
508
|
+
return additionalStaticItems.map(newItem => {
|
|
509
|
+
const existingItem = stateMap.get(newItem.key);
|
|
510
|
+
if (existingItem) {
|
|
511
|
+
// Preserve expanded state but update content
|
|
512
|
+
return {
|
|
513
|
+
...newItem,
|
|
514
|
+
expanded: existingItem.expanded,
|
|
515
|
+
items: newItem.items // Always use fresh items from parent
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
return newItem;
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}, [additionalStaticItems]);
|
|
465
523
|
/**
|
|
466
524
|
* Calculate items for node when expanded (lazy loading)
|
|
525
|
+
* Note: additionalStaticItems are already fully loaded, so skip calculation for them
|
|
467
526
|
*/
|
|
468
527
|
const calculateItemsForNode = useCallback(async (node) => {
|
|
528
|
+
// Skip calculation for separator (it has no children)
|
|
529
|
+
if (node.isSeparator) {
|
|
530
|
+
return undefined;
|
|
531
|
+
}
|
|
532
|
+
// Skip calculation for additional static items (they're pre-loaded and should not trigger dynamic loading)
|
|
533
|
+
if (node.isStaticItem || node.isAdditionalContainer || node.isAdditional) {
|
|
534
|
+
return node.items;
|
|
535
|
+
}
|
|
469
536
|
// If it's a document, return existing items
|
|
470
537
|
if (node.isDcmt)
|
|
471
538
|
return node.items;
|
|
@@ -511,45 +578,78 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
511
578
|
};
|
|
512
579
|
// Container rendering
|
|
513
580
|
if (item.isContainer || !item.isDcmt) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
581
|
+
const defaultContainerStyle = {
|
|
582
|
+
display: 'flex',
|
|
583
|
+
alignItems: 'center',
|
|
584
|
+
gap: '10px',
|
|
585
|
+
height: '32px',
|
|
586
|
+
padding: '6px 0',
|
|
587
|
+
opacity: item.isZero ? 0.4 : 1,
|
|
588
|
+
transition: 'opacity 0.2s ease-in-out'
|
|
589
|
+
};
|
|
590
|
+
const containerStyle = customContainerStyle
|
|
591
|
+
? { ...defaultContainerStyle, ...customContainerStyle(item) }
|
|
592
|
+
: defaultContainerStyle;
|
|
593
|
+
const defaultContent = (_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'relative' }, children: _jsx(IconFolder, { color: item.isManyToMany ? '#ff8f44' : TMColors.iconLight, fontSize: 24 }) }), _jsxs(StyledDivHorizontal, { style: { gap: 5, overflow: 'hidden', whiteSpace: 'nowrap', textOverflow: 'ellipsis' }, children: [_jsx("p", { style: {
|
|
523
594
|
whiteSpace: 'nowrap',
|
|
524
595
|
textOverflow: 'ellipsis',
|
|
525
596
|
fontSize: '1rem',
|
|
526
597
|
color: 'inherit'
|
|
527
598
|
}, children: item.name }), _jsx(StyledBadge, { "$backgroundColor": TMColors.info, children: item.items?.length ?? 0 })] })] }));
|
|
599
|
+
const content = customContainerContent
|
|
600
|
+
? customContainerContent(item, defaultContent)
|
|
601
|
+
: defaultContent;
|
|
602
|
+
return (_jsx("div", { style: containerStyle, children: content }));
|
|
528
603
|
}
|
|
529
604
|
// Document rendering with full metadata display
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
605
|
+
const defaultDocumentStyle = {
|
|
606
|
+
minWidth: '90px',
|
|
607
|
+
width: '100%',
|
|
608
|
+
display: 'flex',
|
|
609
|
+
marginLeft: '5px',
|
|
610
|
+
marginTop: '5px',
|
|
611
|
+
marginBottom: '5px',
|
|
612
|
+
gap: '15px',
|
|
613
|
+
alignItems: 'center',
|
|
614
|
+
cursor: 'pointer',
|
|
615
|
+
userSelect: 'none',
|
|
616
|
+
opacity: item.isZero ? 0.4 : 1,
|
|
617
|
+
transition: 'opacity 0.2s ease-in-out'
|
|
618
|
+
};
|
|
619
|
+
const documentStyle = customDocumentStyle
|
|
620
|
+
? { ...defaultDocumentStyle, ...customDocumentStyle(item) }
|
|
621
|
+
: defaultDocumentStyle;
|
|
622
|
+
const defaultMetadataContent = item.values && (_jsx(StyledDivHorizontal, { style: {
|
|
623
|
+
whiteSpace: 'nowrap',
|
|
624
|
+
textOverflow: 'ellipsis',
|
|
625
|
+
fontSize: '1rem',
|
|
626
|
+
}, children: getDcmtDisplayValue(item.values).map((key, index) => {
|
|
627
|
+
const md = item.values?.[key]?.md;
|
|
628
|
+
const value = item.values?.[key]?.value;
|
|
629
|
+
return (_jsxs(StyledDivHorizontal, { 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(TMUserIdViewer, { userId: value, showIcon: true, noneSelectionText: '' })) : (_jsx("span", { style: { fontWeight: 500 }, children: value }))] }, `${key}_${index}`));
|
|
630
|
+
}) }));
|
|
631
|
+
const metadataContent = customDocumentContent
|
|
632
|
+
? customDocumentContent(item, defaultMetadataContent || _jsx(_Fragment, {}))
|
|
633
|
+
: defaultMetadataContent;
|
|
634
|
+
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] }));
|
|
635
|
+
}, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customContainerStyle, customContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames]);
|
|
636
|
+
/**
|
|
637
|
+
* Wrapper renderer that handles custom rendering if provided
|
|
638
|
+
*/
|
|
639
|
+
const finalItemRender = useCallback((item) => {
|
|
640
|
+
if (!item)
|
|
641
|
+
return _jsx("span", { children: "-" });
|
|
642
|
+
// If customItemRender is provided, try it first
|
|
643
|
+
if (customItemRender) {
|
|
644
|
+
const customResult = customItemRender(item);
|
|
645
|
+
// If custom renderer returns something (not undefined), use it
|
|
646
|
+
if (customResult !== undefined) {
|
|
647
|
+
return customResult;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
// Otherwise use default renderer
|
|
651
|
+
return defaultItemRender(item);
|
|
652
|
+
}, [customItemRender, defaultItemRender]);
|
|
553
653
|
/**
|
|
554
654
|
* Handle focused item changed
|
|
555
655
|
*/
|
|
@@ -562,12 +662,24 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
562
662
|
const handleSelectedItemsChanged = useCallback((items) => {
|
|
563
663
|
onSelectedItemsChanged?.(items);
|
|
564
664
|
}, [onSelectedItemsChanged]);
|
|
665
|
+
/**
|
|
666
|
+
* Handle data changed - separate static items from dynamic tree data
|
|
667
|
+
*/
|
|
668
|
+
const handleDataChanged = useCallback((updatedData) => {
|
|
669
|
+
// Separate static items, separator, and dynamic correlations
|
|
670
|
+
const dynamicItems = updatedData.filter(item => !item.isStaticItem && !item.isSeparator);
|
|
671
|
+
const staticItems = updatedData.filter(item => item.isStaticItem);
|
|
672
|
+
setTreeData(dynamicItems);
|
|
673
|
+
setStaticItemsState(staticItems); // Preserve static items state (expanded/collapsed)
|
|
674
|
+
// Mark that user has interacted with the tree (expanded/collapsed nodes)
|
|
675
|
+
userInteractedWithStaticItemsRef.current = true;
|
|
676
|
+
}, []);
|
|
565
677
|
if (showWaitPanel) {
|
|
566
678
|
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); } }) });
|
|
567
679
|
}
|
|
568
|
-
if (
|
|
680
|
+
if (mergedTreeData.length === 0) {
|
|
569
681
|
return _jsx("div", { style: { padding: '20px', textAlign: 'center', color: '#666' }, children: "Nessuna relazione disponibile." });
|
|
570
682
|
}
|
|
571
|
-
return (_jsx(TMTreeView, { dataSource:
|
|
683
|
+
return (_jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, onSelectionChanged: handleSelectedItemsChanged }));
|
|
572
684
|
};
|
|
573
685
|
export default TMRelationViewer;
|
|
@@ -26,6 +26,7 @@ interface ITMSearchProps {
|
|
|
26
26
|
value: string;
|
|
27
27
|
}>, tid?: number) => void;
|
|
28
28
|
onCurrentTIDChangedCallback?: (tid: number | undefined) => void;
|
|
29
|
+
onlyShowSearchQueryPanel?: boolean;
|
|
29
30
|
}
|
|
30
31
|
declare const TMSearch: React.FunctionComponent<ITMSearchProps>;
|
|
31
32
|
export default TMSearch;
|
|
@@ -18,7 +18,7 @@ var TMSearchViews;
|
|
|
18
18
|
TMSearchViews[TMSearchViews["Search"] = 0] = "Search";
|
|
19
19
|
TMSearchViews[TMSearchViews["Result"] = 1] = "Result";
|
|
20
20
|
})(TMSearchViews || (TMSearchViews = {}));
|
|
21
|
-
const TMSearch = ({ openInOffice, isVisible, inputTID, inputSqdID, inputMids, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, floatingActionConfig, onFileOpened, onRefreshAfterAddDcmtToFavs, onTaskCreateRequest, openWGsCopyMoveForm, openEditPdf, openS4TViewer, onOpenS4TViewerRequest, showTodoDcmtForm, passToArchiveCallback, onCurrentTIDChangedCallback }) => {
|
|
21
|
+
const TMSearch = ({ openInOffice, isVisible, inputTID, inputSqdID, inputMids, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, floatingActionConfig, onFileOpened, onRefreshAfterAddDcmtToFavs, onTaskCreateRequest, openWGsCopyMoveForm, openEditPdf, openS4TViewer, onOpenS4TViewerRequest, showTodoDcmtForm, passToArchiveCallback, onCurrentTIDChangedCallback, onlyShowSearchQueryPanel }) => {
|
|
22
22
|
const [allSQDs, setAllSQDs] = useState([]);
|
|
23
23
|
const [filteredByTIDSQDs, setFilteredByTIDSQDs] = useState([]);
|
|
24
24
|
const [currentSQD, setCurrentSQD] = useState();
|
|
@@ -30,6 +30,7 @@ const TMSearch = ({ openInOffice, isVisible, inputTID, inputSqdID, inputMids, is
|
|
|
30
30
|
const [currentSearchView, setCurrentSearchView] = useState(TMSearchViews.Search);
|
|
31
31
|
const [currentSQDMode, setCurrentSQDMode] = useState(1);
|
|
32
32
|
const [lastQdSearched, setLastQdSearched] = useState();
|
|
33
|
+
const [showSearchResults, setShowSearchResults] = useState(true);
|
|
33
34
|
const deviceType = useDeviceType();
|
|
34
35
|
useEffect(() => {
|
|
35
36
|
if (onCurrentTIDChangedCallback) {
|
|
@@ -76,6 +77,13 @@ const TMSearch = ({ openInOffice, isVisible, inputTID, inputSqdID, inputMids, is
|
|
|
76
77
|
});
|
|
77
78
|
}
|
|
78
79
|
}, [currentSQD]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (onlyShowSearchQueryPanel === undefined)
|
|
82
|
+
return;
|
|
83
|
+
if (onlyShowSearchQueryPanel) {
|
|
84
|
+
setShowSearchResults(false);
|
|
85
|
+
}
|
|
86
|
+
}, [onlyShowSearchQueryPanel]);
|
|
79
87
|
const loadDataSQDsAsync = async (refreshCache, curTID) => {
|
|
80
88
|
if (refreshCache) {
|
|
81
89
|
SavedQueryCacheService.RemoveAll();
|
|
@@ -162,10 +170,11 @@ const TMSearch = ({ openInOffice, isVisible, inputTID, inputSqdID, inputMids, is
|
|
|
162
170
|
SDKUI_Globals.userSettings.searchSettings.mruTIDs = newMruTIDS;
|
|
163
171
|
setMruTIDs(newMruTIDS);
|
|
164
172
|
setCurrentMruTID(fromTID);
|
|
173
|
+
setShowSearchResults(true);
|
|
165
174
|
}, onSqdSaved: async (newSqd) => {
|
|
166
175
|
await loadDataSQDsAsync(true, newSqd.masterTID);
|
|
167
176
|
await setSQDAsync(newSqd);
|
|
168
|
-
} }), [fromDTD, currentSQD, isExpertMode, mruTIDs, searchResult, passToArchiveCallback, inputMids]);
|
|
177
|
+
} }), [fromDTD, showSearchResults, setShowSearchResults, currentSQD, isExpertMode, mruTIDs, searchResult, passToArchiveCallback, inputMids]);
|
|
169
178
|
const tmSavedQuerySelectorElement = useMemo(() => _jsxs(TabPanel, { width: "100%", height: "100%", showNavButtons: true, repaintChangesOnly: true, selectedIndex: currentSQDMode, onSelectedIndexChange: (index) => setCurrentSQDMode(index), children: [(currentTID || currentSQD) ? _jsx(Item, { title: fromDTD?.nameLoc, children: _jsx(TMSavedQuerySelectorWrapper, { allowShowSearch: false, items: filteredByTIDSQDs, selectedId: currentSQD?.id, onRefreshData: () => { loadDataSQDsAsync(true); }, onItemClick: (sqd) => {
|
|
170
179
|
onSQDItemClick(sqd, setSQDAsync);
|
|
171
180
|
}, onDeleted: (sqd) => onSQDDeleted(sqd, sqd.id == currentSQD?.id ? filteredByTIDSQDs.find(o => o.id == 1) : currentSQD, setSQDAsync) }) }) : _jsx(_Fragment, {}), _jsx(Item, { title: SDKUI_Localizator.Alls2, children: _jsx(TMSavedQuerySelectorWrapper, { allowShowSearch: true, items: allSQDs, manageDefault: false, onItemClick: (sqd) => {
|
|
@@ -209,8 +218,8 @@ const TMSearch = ({ openInOffice, isVisible, inputTID, inputSqdID, inputMids, is
|
|
|
209
218
|
contentOptions: { component: tmSavedQuerySelectorElement, panelContainer: { title: SDK_Localizator.SavedQueries } },
|
|
210
219
|
toolbarOptions: { icon: _jsx(IconSavedQuery, { fontSize: 24 }), visible: true, orderNumber: 4, isActive: allInitialPanelVisibility['TMSavedQuerySelector'] }
|
|
211
220
|
}
|
|
212
|
-
], [tmTreeSelectorElement, tmRecentsManagerElement, tmSearchQueryPanelElement, tmSavedQuerySelectorElement, fromDTD, mruTIDs]);
|
|
213
|
-
return (_jsxs(_Fragment, { children: [_jsx(StyledMultiViewPanel, { "$isVisible": currentSearchView === TMSearchViews.Search, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'TMRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }), _jsx(TMSearchResult, { isVisible: isVisible && currentSearchView === TMSearchViews.Result, context: SearchResultContext.METADATA_SEARCH, searchResults: searchResult, floatingActionConfig: floatingActionConfig, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, openInOffice: openInOffice, onRefreshSearchAsync: onRefreshSearchAsync, onClose: () => { setCurrentSearchView(TMSearchViews.Search); }, onFileOpened: onFileOpened, onTaskCreateRequest: onTaskCreateRequest, openWGsCopyMoveForm: openWGsCopyMoveForm, openEditPdf: openEditPdf, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, passToArchiveCallback: passToArchiveCallback, onSelectedTIDChanged: onCurrentTIDChangedCallback, showTodoDcmtForm: showTodoDcmtForm })] }));
|
|
221
|
+
], [tmTreeSelectorElement, showSearchResults, tmRecentsManagerElement, tmSearchQueryPanelElement, tmSavedQuerySelectorElement, fromDTD, mruTIDs]);
|
|
222
|
+
return (_jsxs(_Fragment, { children: [showSearchResults ? _jsx(StyledMultiViewPanel, { "$isVisible": currentSearchView === TMSearchViews.Search, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'TMRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }) : tmSearchQueryPanelElement, showSearchResults && _jsx(TMSearchResult, { isVisible: isVisible && currentSearchView === TMSearchViews.Result, context: SearchResultContext.METADATA_SEARCH, searchResults: searchResult, floatingActionConfig: floatingActionConfig, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, openInOffice: openInOffice, onRefreshSearchAsync: onRefreshSearchAsync, onClose: () => { onlyShowSearchQueryPanel ? setShowSearchResults(false) : setCurrentSearchView(TMSearchViews.Search); }, onFileOpened: onFileOpened, onTaskCreateRequest: onTaskCreateRequest, openWGsCopyMoveForm: openWGsCopyMoveForm, openEditPdf: openEditPdf, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, passToArchiveCallback: passToArchiveCallback, onSelectedTIDChanged: onCurrentTIDChangedCallback, showTodoDcmtForm: showTodoDcmtForm })] }));
|
|
214
223
|
};
|
|
215
224
|
export default TMSearch;
|
|
216
225
|
const TMTreeSelectorWrapper = ({ isMobile, onSelectedTIDChanged }) => {
|