@topconsultnpm/sdkui-react 6.22.0-dev3.24 → 6.22.0-dev3.25
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/TMAlert.js +6 -8
- package/lib/components/base/TMAreaManager.js +53 -55
- package/lib/components/base/TMCustomButton.js +39 -48
- package/lib/components/base/TMTreeView.js +14 -0
- package/lib/components/base/waitPanel/TMWaitPanelConfirm.d.ts +12 -0
- package/lib/components/base/waitPanel/TMWaitPanelConfirm.js +185 -0
- package/lib/components/base/waitPanel/TMWaitPanelManager.d.ts +169 -0
- package/lib/components/base/waitPanel/TMWaitPanelManager.js +320 -0
- package/lib/components/base/waitPanel/TMWaitPanelView.d.ts +53 -0
- package/lib/components/base/waitPanel/TMWaitPanelView.js +274 -0
- package/lib/components/base/waitPanel/index.d.ts +1 -0
- package/lib/components/base/waitPanel/index.js +1 -0
- package/lib/components/choosers/TMPathChooser.js +17 -34
- package/lib/components/features/assistant/TMToppyDraggableHelpCenter.js +2 -2
- package/lib/components/features/documents/TMBatchUpdateForm.js +3 -4
- package/lib/components/features/documents/TMCopyToFolderForm.js +32 -35
- package/lib/components/features/documents/TMDcmtForm.js +48 -60
- package/lib/components/features/documents/TMDcmtIcon.d.ts +1 -3
- package/lib/components/features/documents/TMDcmtIcon.js +3 -5
- package/lib/components/features/documents/TMDcmtPreview.js +5 -3
- package/lib/components/features/documents/TMMasterDetailDcmts.d.ts +2 -0
- package/lib/components/features/documents/TMMasterDetailDcmts.js +45 -147
- package/lib/components/features/documents/TMMergeToPdfForm.js +48 -47
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +14 -15
- package/lib/components/features/documents/TMRelationViewer.d.ts +3 -14
- package/lib/components/features/documents/TMRelationViewer.js +115 -79
- package/lib/components/features/search/TMSearchResult.js +6 -8
- package/lib/components/features/search/TMViewHistoryDcmt.js +14 -25
- package/lib/components/features/tasks/TMTasksView.js +91 -90
- package/lib/components/features/wg/TMWGsCopyMoveForm.js +91 -91
- package/lib/components/grids/TMBlogsPost.js +92 -96
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/pages/TMPage.js +29 -31
- package/lib/helper/SDKUI_Localizator.d.ts +7 -0
- package/lib/helper/SDKUI_Localizator.js +70 -0
- package/lib/helper/ZipManager.d.ts +1 -1
- package/lib/helper/ZipManager.js +2 -1
- package/lib/hooks/useCheckInOutOperations.d.ts +0 -6
- package/lib/hooks/useCheckInOutOperations.js +14 -34
- package/lib/hooks/useDcmtOperations.d.ts +3 -12
- package/lib/hooks/useDcmtOperations.js +130 -160
- package/lib/hooks/useDocumentOperations.js +12 -23
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +0 -15
- package/lib/hooks/useMultiMasterDetailDcmts.js +62 -65
- package/lib/hooks/useWorkflowApprove.js +33 -17
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { DcmtTypeListCacheService, SDK_Globals, DataColumnTypes, MetadataDataDomains, RelationCacheService, RelationTypes, UserListCacheService, LayoutModes } from "@topconsultnpm/sdk-ts";
|
|
4
4
|
import { genUniqueId, IconFolder, IconBackhandIndexPointingRight, IconCircleInfo, getDcmtCicoStatus, IconChevronDown, IconChevronRight, SDKUI_Localizator, buildDcmtDisplayName, SDKUI_Globals, searchResultToMetadataValues, getColumnFormatInfo, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat } from '../../../helper';
|
|
5
5
|
import ShowAlert from '../../base/TMAlert';
|
|
@@ -7,7 +7,7 @@ import TMToppyMessage from '../../../helper/TMToppyMessage';
|
|
|
7
7
|
import { TMColors } from '../../../utils/theme';
|
|
8
8
|
import { StyledDivHorizontal, StyledBadge } from '../../base/Styled';
|
|
9
9
|
import TMTreeView from '../../base/TMTreeView';
|
|
10
|
-
import
|
|
10
|
+
import TMWaitPanelManager from '../../base/waitPanel/TMWaitPanelManager';
|
|
11
11
|
import TMDataListItemViewer from '../../viewers/TMDataListItemViewer';
|
|
12
12
|
import TMDcmtIcon from './TMDcmtIcon';
|
|
13
13
|
import TMDataUserIdItemViewer from '../../viewers/TMDataUserIdItemViewer';
|
|
@@ -66,12 +66,24 @@ export const getDisplayValueByColumn = (col, value) => {
|
|
|
66
66
|
return formatNumberByMetadataFormat(value, format, formatCulture);
|
|
67
67
|
return value;
|
|
68
68
|
};
|
|
69
|
+
/**
|
|
70
|
+
* Attende la chiamata, ma smette di aspettarla (restituendo undefined) appena l'utente interrompe.
|
|
71
|
+
* Serve perché il SearchEngine dell'SDK non accetta un AbortSignal: senza, "Interrompi" aspetterebbe la fine della chiamata in corso.
|
|
72
|
+
*/
|
|
73
|
+
const untilAborted = (promise, signal) => {
|
|
74
|
+
if (!signal)
|
|
75
|
+
return promise;
|
|
76
|
+
if (signal.aborted)
|
|
77
|
+
return Promise.resolve(undefined);
|
|
78
|
+
const aborted = new Promise(resolve => signal.addEventListener('abort', () => resolve(undefined), { once: true }));
|
|
79
|
+
// Vince chi termina prima: la risposta del server oppure l'interruzione (la chiamata prosegue in background e viene ignorata)
|
|
80
|
+
return Promise.race([promise, aborted]);
|
|
81
|
+
};
|
|
69
82
|
/**
|
|
70
83
|
* Convert SearchResultDescriptor to structured data source with metadata
|
|
71
|
-
* I valori dei documenti sono
|
|
84
|
+
* I valori dei documenti sono già presenti nelle righe restituite dalla ricerca:
|
|
72
85
|
* qui vengono solo abbinati ai rispettivi MetadataDescriptor (perm,
|
|
73
|
-
* isSpecialSearchOutput, ...), risolti
|
|
74
|
-
* Nessuna chiamata al server per riga.
|
|
86
|
+
* isSpecialSearchOutput, ...), risolti una sola volta per tipo documento.
|
|
75
87
|
*/
|
|
76
88
|
export const searchResultToDataSource = async (searchResult, hideSysMetadata, signal) => {
|
|
77
89
|
const rows = searchResult?.dtdResult?.rows ?? [];
|
|
@@ -144,7 +156,7 @@ export const searchResultToDataSource = async (searchResult, hideSysMetadata, si
|
|
|
144
156
|
return output;
|
|
145
157
|
};
|
|
146
158
|
export const DEFAULT_RELATION_EXPAND_LEVEL = 4; // Default maximum depth level for expansion (can be adjusted)
|
|
147
|
-
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onAllItemsChanged, onDocumentDoubleClick, customItemRender, customDocumentStyle, customMainContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems, showMainDocument = true, labelMainContainer, onNoRelationsFound, onItemContextMenu,
|
|
159
|
+
const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndicator = true, allowShowZeroDcmts = true, initialShowZeroDcmts = false, allowedTIDs, allowMultipleSelection = false, focusedItem, selectedItems, onFocusedItemChanged, onSelectedItemsChanged, onAllItemsChanged, onDocumentDoubleClick, customItemRender, customDocumentStyle, customMainContainerContent, customDocumentContent, showMetadataNames = false, maxDepthLevel = 2, invertMasterNavigation = true, additionalStaticItems, showMainDocument = true, labelMainContainer, onNoRelationsFound, onItemContextMenu, showExpandAllButton = false, defaultExpandAll = false, onLoadingStateChanged, onStopCalculationReady, onCalculationStopped, initialTreeState, onTreeStateChange }) => {
|
|
148
160
|
// State
|
|
149
161
|
const [dcmtTypes, setDcmtTypes] = useState([]);
|
|
150
162
|
const [treeData, setTreeData] = useState([]);
|
|
@@ -153,21 +165,21 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
153
165
|
const getInitialExpandLevel = () => SDKUI_Globals.userSettings?.searchSettings?.relationExpandLevel ?? DEFAULT_RELATION_EXPAND_LEVEL;
|
|
154
166
|
const [expandLevel, setExpandLevel] = useState(getInitialExpandLevel);
|
|
155
167
|
const [isAllCollapsed, setIsAllCollapsed] = useState(false);
|
|
156
|
-
const initialExpandAllAppliedRef =
|
|
157
|
-
|
|
158
|
-
const [
|
|
159
|
-
const
|
|
160
|
-
const [waitPanelMaxValuePrimary, setWaitPanelMaxValuePrimary] = useState(0);
|
|
168
|
+
const initialExpandAllAppliedRef = useRef(false);
|
|
169
|
+
// Calcolo iniziale: il pannello e' imperativo, qui resta solo il flag per il render.
|
|
170
|
+
const [isInitialLoading, setIsInitialLoading] = useState(false);
|
|
171
|
+
const loadPanelRef = useRef(undefined);
|
|
161
172
|
const [abortController] = useState(new AbortController());
|
|
162
173
|
// True quando l'utente ha interrotto il calcolo delle correlazioni (pulsante "Interrompi")
|
|
163
174
|
const [loadStoppedByUser, setLoadStoppedByUser] = useState(false);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const [
|
|
167
|
-
|
|
168
|
-
const
|
|
175
|
+
// Espansione di un nodo: il pannello e' imperativo, qui resta solo il flag per il render.
|
|
176
|
+
// qui resta solo il flag che serve al render e alla notifica al padre.
|
|
177
|
+
const [isExpansionLoading, setIsExpansionLoading] = useState(false);
|
|
178
|
+
// AbortController dell'espansione in corso, accessibile da callback stabili
|
|
179
|
+
const expansionAbortControllerRef = useRef(undefined);
|
|
180
|
+
const expansionPanelRef = useRef(undefined);
|
|
169
181
|
// Ref to track last loaded input to prevent unnecessary reloads
|
|
170
|
-
const lastLoadedInputRef =
|
|
182
|
+
const lastLoadedInputRef = useRef('');
|
|
171
183
|
// State to track loaded input key - triggers re-render for focus selection
|
|
172
184
|
const [loadedInputKey, setLoadedInputKey] = React.useState('');
|
|
173
185
|
// State to track how many GetMetadataAsync calls failed
|
|
@@ -175,17 +187,17 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
175
187
|
// State to track total master documents found (used when isForMaster is true)
|
|
176
188
|
const [totalMasterDocuments, setTotalMasterDocuments] = React.useState(0);
|
|
177
189
|
// Ref to track if user has manually expanded/collapsed static items
|
|
178
|
-
const userInteractedWithStaticItemsRef =
|
|
190
|
+
const userInteractedWithStaticItemsRef = useRef(false);
|
|
179
191
|
// Ref to track the last inputKey for which we set the focused item
|
|
180
|
-
const lastFocusedInputRef =
|
|
192
|
+
const lastFocusedInputRef = useRef('');
|
|
181
193
|
// Catturato al mount (il padre lo aggiorna di continuo) e azzerato dopo l'uso,
|
|
182
194
|
// così vale solo per il primo caricamento
|
|
183
|
-
const stateToRestoreRef =
|
|
195
|
+
const stateToRestoreRef = useRef(initialTreeState);
|
|
184
196
|
// Parte dallo stato ricevuto: il padre azzera il fuoco prima di rimontare, quindi
|
|
185
197
|
// la prima notifica dopo il rimontaggio lo perderebbe
|
|
186
|
-
const lastReportedStateRef =
|
|
198
|
+
const lastReportedStateRef = useRef(initialTreeState);
|
|
187
199
|
// Percorso su cui rimettere il fuoco dopo un ripristino
|
|
188
|
-
const pendingFocusPathRef =
|
|
200
|
+
const pendingFocusPathRef = useRef(undefined);
|
|
189
201
|
// State for all users (used for checkout status display)
|
|
190
202
|
const [allUsers, setAllUsers] = useState([]);
|
|
191
203
|
// Load all users for checkout status resolution
|
|
@@ -198,25 +210,19 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
198
210
|
}, []);
|
|
199
211
|
// Notify parent when loading state changes
|
|
200
212
|
useEffect(() => {
|
|
201
|
-
const isLoading =
|
|
213
|
+
const isLoading = isInitialLoading || isExpansionLoading;
|
|
202
214
|
onLoadingStateChanged?.(isLoading);
|
|
203
|
-
}, [
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const expansionAbortControllerRef = React.useRef(undefined);
|
|
211
|
-
useEffect(() => {
|
|
212
|
-
expansionAbortControllerRef.current = expansionAbortController;
|
|
213
|
-
}, [expansionAbortController]);
|
|
215
|
+
}, [isInitialLoading, isExpansionLoading, onLoadingStateChanged]);
|
|
216
|
+
// I pannelli vivono fuori dall'albero React: se il viewer viene smontato mentre un
|
|
217
|
+
// caricamento e' in corso vanno chiusi a mano, altrimenti resterebbero a schermo.
|
|
218
|
+
useEffect(() => () => {
|
|
219
|
+
loadPanelRef.current?.close();
|
|
220
|
+
expansionPanelRef.current?.close();
|
|
221
|
+
}, []);
|
|
214
222
|
/**
|
|
215
223
|
* Interrompe il calcolo delle correlazioni (iniziale e/o espansione in corso):
|
|
216
224
|
* non vengono eseguite ulteriori chiamate GetMetadata e vengono mantenute
|
|
217
225
|
* solo le correlazioni già calcolate.
|
|
218
|
-
* È sincrona: può essere invocata immediatamente prima di smontare il viewer
|
|
219
|
-
* (es. pulsante "Indietro"), quando un effect non farebbe in tempo.
|
|
220
226
|
*/
|
|
221
227
|
const stopCalculation = useCallback(() => {
|
|
222
228
|
if (!abortController.signal.aborted)
|
|
@@ -274,7 +280,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
274
280
|
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
275
281
|
if (!searchEngine)
|
|
276
282
|
return items;
|
|
277
|
-
const results = await searchEngine.GetAllDetailDcmtsAsync(mTID, mDID) ?? [];
|
|
283
|
+
const results = await untilAborted(searchEngine.GetAllDetailDcmtsAsync(mTID, mDID), signal) ?? [];
|
|
278
284
|
for (const searchResult of results) {
|
|
279
285
|
// Interruzione richiesta dall'utente: restituisce le correlazioni già calcolate
|
|
280
286
|
if (signal?.aborted)
|
|
@@ -364,7 +370,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
364
370
|
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
365
371
|
if (!searchEngine)
|
|
366
372
|
return items;
|
|
367
|
-
const results = await searchEngine.GetAllMasterDcmtsAsync(dTID, dDID) ?? [];
|
|
373
|
+
const results = await untilAborted(searchEngine.GetAllMasterDcmtsAsync(dTID, dDID), signal) ?? [];
|
|
368
374
|
for (const searchResult of results) {
|
|
369
375
|
// Interruzione richiesta dall'utente: restituisce le correlazioni già calcolate
|
|
370
376
|
if (signal?.aborted)
|
|
@@ -610,7 +616,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
610
616
|
/**
|
|
611
617
|
* Main data loading function
|
|
612
618
|
*/
|
|
613
|
-
const loadData = useCallback(async () => {
|
|
619
|
+
const loadData = useCallback(async (panel) => {
|
|
614
620
|
if (!inputDcmts || inputDcmts.length === 0 || dcmtTypes.length === 0) {
|
|
615
621
|
setTreeData([]);
|
|
616
622
|
return;
|
|
@@ -625,14 +631,14 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
625
631
|
break;
|
|
626
632
|
}
|
|
627
633
|
// Update wait panel progress
|
|
628
|
-
|
|
634
|
+
panel.setSubtitle(`Caricamento correlazione ${processedCount + 1} di ${inputDcmts.length}`);
|
|
629
635
|
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
630
636
|
if (!searchEngine)
|
|
631
637
|
continue;
|
|
632
638
|
// Load document metadata
|
|
633
639
|
let result;
|
|
634
640
|
try {
|
|
635
|
-
result = await searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false);
|
|
641
|
+
result = await untilAborted(searchEngine.GetMetadataAsync(dcmt.TID, dcmt.DID, false), abortController.signal);
|
|
636
642
|
}
|
|
637
643
|
catch (metadataError) {
|
|
638
644
|
console.warn('[TMRelationViewer] GetMetadataAsync failed for TID:', dcmt.TID, 'DID:', dcmt.DID, metadataError);
|
|
@@ -645,9 +651,12 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
645
651
|
});
|
|
646
652
|
// Skip this document and continue with the next one
|
|
647
653
|
processedCount++;
|
|
648
|
-
|
|
654
|
+
await panel.step();
|
|
649
655
|
continue;
|
|
650
656
|
}
|
|
657
|
+
// Interrotto durante la chiamata: esce con le correlazioni gia' calcolate
|
|
658
|
+
if (abortController.signal.aborted)
|
|
659
|
+
break;
|
|
651
660
|
const dtd = await DcmtTypeListCacheService.GetAsync(dcmt.TID);
|
|
652
661
|
// Parse document values
|
|
653
662
|
const docValues = await searchResultToDataSource(result, undefined, abortController.signal);
|
|
@@ -823,7 +832,8 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
823
832
|
}
|
|
824
833
|
// Update progress counter
|
|
825
834
|
processedCount++;
|
|
826
|
-
|
|
835
|
+
// step() attende anche la conferma di interruzione: col dialogo aperto il ciclo resta fermo
|
|
836
|
+
await panel.step();
|
|
827
837
|
}
|
|
828
838
|
/**
|
|
829
839
|
* Helper function to check if a node has any actual correlated documents
|
|
@@ -973,20 +983,28 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
973
983
|
lastLoadedInputRef.current = currentKey;
|
|
974
984
|
// Reset interaction flag when loading new data
|
|
975
985
|
userInteractedWithStaticItemsRef.current = false;
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
986
|
+
setIsInitialLoading(true);
|
|
987
|
+
const panel = TMWaitPanelManager.show({
|
|
988
|
+
title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments,
|
|
989
|
+
max: inputDcmts.length,
|
|
990
|
+
showCounters: false,
|
|
991
|
+
cancelable: true,
|
|
992
|
+
onAbort: () => abortController.abort(),
|
|
993
|
+
});
|
|
994
|
+
loadPanelRef.current = panel;
|
|
979
995
|
// Call loadData and use .then() instead of await to allow React to render
|
|
980
|
-
loadData().then(() => {
|
|
996
|
+
loadData(panel).then(() => {
|
|
981
997
|
// Mark as loaded AFTER data is ready - state update triggers re-render for focus selection
|
|
982
998
|
lastLoadedInputRef.current = currentKey;
|
|
983
999
|
setLoadedInputKey(currentKey);
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1000
|
+
}).finally(() => {
|
|
1001
|
+
// Il pannello resta aperto per tutto loadData, ripristini inclusi
|
|
1002
|
+
panel.close();
|
|
1003
|
+
if (loadPanelRef.current === panel)
|
|
1004
|
+
loadPanelRef.current = undefined;
|
|
1005
|
+
setIsInitialLoading(false);
|
|
988
1006
|
});
|
|
989
|
-
}, [inputDcmts, dcmtTypes, maxDepthLevel, getInputKey, loadData, treeData.length]);
|
|
1007
|
+
}, [inputDcmts, dcmtTypes, maxDepthLevel, isForMaster, abortController, getInputKey, loadData, treeData.length]);
|
|
990
1008
|
/**
|
|
991
1009
|
* Update tree when showZeroDcmts changes
|
|
992
1010
|
* Updates the visualization without re-fetching data (isLoaded stays true)
|
|
@@ -1123,11 +1141,19 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1123
1141
|
// ============================================
|
|
1124
1142
|
if (node.isDcmt && node.tid && node.did) {
|
|
1125
1143
|
const newAbortController = new AbortController();
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1144
|
+
expansionAbortControllerRef.current = newAbortController;
|
|
1145
|
+
setIsExpansionLoading(true);
|
|
1146
|
+
// Un solo nodo: niente contatori ne' stima dei tempi, solo l'attesa annullabile.
|
|
1147
|
+
const panel = TMWaitPanelManager.show({
|
|
1148
|
+
title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments,
|
|
1149
|
+
subtitle: 'Caricamento documenti correlati...',
|
|
1150
|
+
max: 1,
|
|
1151
|
+
showCounters: false,
|
|
1152
|
+
showEta: false,
|
|
1153
|
+
cancelable: true,
|
|
1154
|
+
onAbort: () => newAbortController.abort(),
|
|
1155
|
+
});
|
|
1156
|
+
expansionPanelRef.current = panel;
|
|
1131
1157
|
try {
|
|
1132
1158
|
// Check for abort
|
|
1133
1159
|
if (newAbortController.signal.aborted) {
|
|
@@ -1163,8 +1189,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1163
1189
|
return [];
|
|
1164
1190
|
}
|
|
1165
1191
|
finally {
|
|
1166
|
-
|
|
1167
|
-
|
|
1192
|
+
panel.close();
|
|
1193
|
+
setIsExpansionLoading(false);
|
|
1194
|
+
expansionAbortControllerRef.current = undefined;
|
|
1195
|
+
expansionPanelRef.current = undefined;
|
|
1168
1196
|
}
|
|
1169
1197
|
}
|
|
1170
1198
|
// Default: return existing items
|
|
@@ -1292,7 +1320,7 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1292
1320
|
checkoutStatusIcon = checkoutStatus.icon;
|
|
1293
1321
|
}
|
|
1294
1322
|
}
|
|
1295
|
-
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'
|
|
1323
|
+
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' })), checkoutStatusIcon, metadataContent] }));
|
|
1296
1324
|
}, [onDocumentDoubleClick, showCurrentDcmtIndicator, inputDcmts, customMainContainerContent, customDocumentStyle, customDocumentContent, showMetadataNames, showMainDocument, allUsers]);
|
|
1297
1325
|
/**
|
|
1298
1326
|
* Wrapper renderer that handles custom rendering if provided
|
|
@@ -1406,10 +1434,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1406
1434
|
}
|
|
1407
1435
|
const targetLevel = expandLevel;
|
|
1408
1436
|
const newAbortController = new AbortController();
|
|
1409
|
-
|
|
1410
|
-
//
|
|
1411
|
-
|
|
1412
|
-
|
|
1437
|
+
expansionAbortControllerRef.current = newAbortController;
|
|
1438
|
+
// Aperto solo se servono chiamate API (vedi hasApiCalls): con i figli già in
|
|
1439
|
+
// memoria l'espansione è immediata e il pannello lampeggerebbe soltanto.
|
|
1440
|
+
let panel;
|
|
1413
1441
|
// CONTATORI DINAMICI:
|
|
1414
1442
|
// - totalNodes: cresce quando scopriamo nuovi nodi (evita che percentuale > 100%)
|
|
1415
1443
|
// - processedNodes: quanti nodi abbiamo già processato
|
|
@@ -1487,9 +1515,17 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1487
1515
|
if (!hasApiCalls) {
|
|
1488
1516
|
hasApiCalls = true;
|
|
1489
1517
|
TMSpinner.hide();
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1518
|
+
setIsExpansionLoading(true);
|
|
1519
|
+
// Avanzamento in centesimi: il totale cambia mentre si scoprono i figli, niente ETA.
|
|
1520
|
+
panel = TMWaitPanelManager.show({
|
|
1521
|
+
title: isForMaster ? SDKUI_Localizator.LoadingMasterDocuments : SDKUI_Localizator.LoadingDetailDocuments,
|
|
1522
|
+
max: 100,
|
|
1523
|
+
showCounters: false,
|
|
1524
|
+
showEta: false,
|
|
1525
|
+
cancelable: true,
|
|
1526
|
+
onAbort: () => newAbortController.abort(),
|
|
1527
|
+
});
|
|
1528
|
+
expansionPanelRef.current = panel;
|
|
1493
1529
|
}
|
|
1494
1530
|
try {
|
|
1495
1531
|
let loadedItems = [];
|
|
@@ -1537,9 +1573,9 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1537
1573
|
shownProgress = Math.min(99, realProgress > shownProgress
|
|
1538
1574
|
? realProgress
|
|
1539
1575
|
: shownProgress + (99 - shownProgress) * 0.01);
|
|
1540
|
-
|
|
1576
|
+
panel?.setValue(shownProgress);
|
|
1541
1577
|
// Seconda riga: spiega perché percentuale e conteggio non vanno di pari passo
|
|
1542
|
-
|
|
1578
|
+
panel?.setSubtitle(SDKUI_Localizator.ExpansionProgress.replaceParams(processedNodes.toString(), totalNodes.toString())
|
|
1543
1579
|
+ (totalNodes > initialTotalNodes ? `\n${SDKUI_Localizator.ExpansionProgressIsEstimated}` : ''));
|
|
1544
1580
|
// ═══════════════════════════════════════════════════════════════════
|
|
1545
1581
|
// PASSO 3: CHIAMATA RICORSIVA SUI FIGLI
|
|
@@ -1568,8 +1604,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1568
1604
|
finally {
|
|
1569
1605
|
// Cleanup: nascondi spinner e pannello di attesa
|
|
1570
1606
|
TMSpinner.hide();
|
|
1571
|
-
|
|
1572
|
-
|
|
1607
|
+
panel?.close();
|
|
1608
|
+
setIsExpansionLoading(false);
|
|
1609
|
+
expansionAbortControllerRef.current = undefined;
|
|
1610
|
+
expansionPanelRef.current = undefined;
|
|
1573
1611
|
}
|
|
1574
1612
|
}, [expandLevel, treeData, staticItemsState, isForMaster, invertMasterNavigation, getDetailDcmtsAsync, getMasterDcmtsAsync, updateHiddenProperty, applyZeroFilterInfoMessage]);
|
|
1575
1613
|
/**
|
|
@@ -1600,8 +1638,10 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1600
1638
|
// Reset collapsed state when user manually expands a node
|
|
1601
1639
|
setIsAllCollapsed(false);
|
|
1602
1640
|
}, []);
|
|
1603
|
-
|
|
1604
|
-
|
|
1641
|
+
// Il pannello di attesa è montato dal manager fuori dall'albero React: qui resta
|
|
1642
|
+
// solo il posto dell'albero, che compare a calcolo finito.
|
|
1643
|
+
if (isInitialLoading) {
|
|
1644
|
+
return _jsx("div", { style: { width: '100%', height: '100%' } });
|
|
1605
1645
|
}
|
|
1606
1646
|
if (mergedTreeData.length === 0) {
|
|
1607
1647
|
// Calcolo interrotto dall'utente prima di aver calcolato qualsiasi correlazione
|
|
@@ -1658,15 +1698,11 @@ const TMRelationViewer = ({ inputDcmts, isForMaster = false, showCurrentDcmtIndi
|
|
|
1658
1698
|
whiteSpace: 'nowrap',
|
|
1659
1699
|
outline: 'none',
|
|
1660
1700
|
transition: 'background 0.2s'
|
|
1661
|
-
}, children: [_jsx(IconChevronRight, { fontSize: 14 }), _jsx("span", { children: SDKUI_Localizator.CollapseAll })] }) })] })), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, itemsPerPage: 100, onSelectionChanged: handleSelectedItemsChanged, onItemContextMenu:
|
|
1701
|
+
}, children: [_jsx(IconChevronRight, { fontSize: 14 }), _jsx("span", { children: SDKUI_Localizator.CollapseAll })] }) })] })), _jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'auto' }, children: _jsx(TMTreeView, { dataSource: mergedTreeData, itemRender: finalItemRender, calculateItemsForNode: calculateItemsForNode, onDataChanged: handleDataChanged, focusedItem: focusedItem, onFocusedItemChanged: handleFocusedItemChanged, allowMultipleSelection: allowMultipleSelection, selectedItems: selectedItems, itemsPerPage: 100, onSelectionChanged: handleSelectedItemsChanged, onItemContextMenu: isExpansionLoading ? undefined : onItemContextMenu, enableVirtualization: true, shouldDelayFocusOnEvent: (node, e) => {
|
|
1662
1702
|
// Ritarda il focus quando si clicca sull'icona del documento
|
|
1663
1703
|
// per permettere al doppio click di funzionare
|
|
1664
1704
|
const target = e.target;
|
|
1665
1705
|
return !!target.closest('.tm-dcmt-icon');
|
|
1666
|
-
} }) })
|
|
1667
|
-
setTimeout(() => {
|
|
1668
|
-
expansionAbortController?.abort();
|
|
1669
|
-
}, 100);
|
|
1670
|
-
} }))] }));
|
|
1706
|
+
} }) })] }));
|
|
1671
1707
|
};
|
|
1672
1708
|
export default TMRelationViewer;
|
|
@@ -16,11 +16,10 @@ import TMAccordionNew from '../../base/TMAccordionNew';
|
|
|
16
16
|
import TMButton from '../../base/TMButton';
|
|
17
17
|
import TMDataGrid, { TMDataGridPageSize } from '../../base/TMDataGrid';
|
|
18
18
|
import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
|
|
19
|
-
import { TMLayoutItem, TMSplitterLayout } from '../../base/TMLayout';
|
|
19
|
+
import TMLayoutContainer, { TMLayoutItem, TMSplitterLayout } from '../../base/TMLayout';
|
|
20
20
|
import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../../base/TMPopUp';
|
|
21
21
|
import TMSpinner from '../../base/TMSpinner';
|
|
22
22
|
import { StyledMultiViewPanel } from '../../base/Styled';
|
|
23
|
-
import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
|
|
24
23
|
import TMMetadataValues from '../../editors/TMMetadataValues';
|
|
25
24
|
import TMTidViewer from '../../viewers/TMTidViewer';
|
|
26
25
|
import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
|
|
@@ -34,7 +33,6 @@ import TMDcmtPreview from '../documents/TMDcmtPreview';
|
|
|
34
33
|
import { WorkFlowOperationButtons } from '../workflow/TMWorkflowPopup';
|
|
35
34
|
// Local
|
|
36
35
|
import TMSearchResultFloatingActionButton from './TMSearchResultFloatingActionButton';
|
|
37
|
-
let abortControllerLocal = new AbortController();
|
|
38
36
|
//#region Helper Methods
|
|
39
37
|
export const getSearchResultCountersSingleCategory = (searchResults) => {
|
|
40
38
|
// let totDcmtTypes = searchResults.length;
|
|
@@ -397,7 +395,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
397
395
|
openTaskFormHandler,
|
|
398
396
|
},
|
|
399
397
|
});
|
|
400
|
-
const { isOpenDcmtForm, isDcmtFormModal, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid, showDcmtTypesSelection, showExportForm, updateBatchUpdateForm, closeDcmtFormHandler, handleSignApprove,
|
|
398
|
+
const { isOpenDcmtForm, isDcmtFormModal, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid, showDcmtTypesSelection, showExportForm, updateBatchUpdateForm, closeDcmtFormHandler, handleSignApprove, dcmtOperations: { downloadDcmtsAsync, runOperationAsync, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
|
|
401
399
|
// ----- Azioni della colonna di riga (showActionsColumn) -----------------------------------------
|
|
402
400
|
// Le voci di operationItems agiscono sui documenti selezionati o, in mancanza, su quello a fuoco:
|
|
403
401
|
// non accettano una riga come argomento. Il click sull'icona quindi azzera la selezione e porta il
|
|
@@ -987,10 +985,10 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
987
985
|
gap: Gutters.getGutters(),
|
|
988
986
|
width: '100%',
|
|
989
987
|
height: '100%',
|
|
990
|
-
}, children: _jsx(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
988
|
+
}, children: _jsx(TMLayoutContainer, { direction: 'vertical', children: (groupId && groupId.length > 0) ? panelManagerContent
|
|
989
|
+
// Senza le prop di persistenza il provider si comporta come quello base:
|
|
990
|
+
// i contesti che non ricordano il layout restano come prima
|
|
991
|
+
: _jsx(TMPanelManagerWithPersistenceProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmSearchResult', isPersistenceEnabled: isLayoutPersisted && hasSearchResultPanelLayout(), persistPanelStates: isLayoutPersisted ? (state) => saveSearchResultPanelLayout(state, disabledLayoutPanelIds) : undefined, persistedPanelStates: isLayoutPersisted ? getSearchResultPanelLayout() : undefined, children: panelManagerContent }) }) }), renderDcmtOperations] }));
|
|
994
992
|
};
|
|
995
993
|
export default TMSearchResult;
|
|
996
994
|
/** Cerca una voce di operazione per id, anche dentro i sottomenu */
|
|
@@ -10,12 +10,13 @@ import TMDcmtIcon from "../documents/TMDcmtIcon";
|
|
|
10
10
|
import TMDataGrid from "../../base/TMDataGrid";
|
|
11
11
|
import TMDcmtForm from "../documents/TMDcmtForm";
|
|
12
12
|
import { useDcmtOperations } from "../../../hooks/useDcmtOperations";
|
|
13
|
-
import
|
|
13
|
+
import TMLayoutContainer from "../../base/TMLayout";
|
|
14
|
+
import TMWaitPanelManager from "../../base/waitPanel/TMWaitPanelManager";
|
|
14
15
|
import { TMResultManager } from "../../forms/TMResultDialog";
|
|
15
16
|
let abortLocalController = new AbortController();
|
|
16
17
|
const TMViewHistoryDcmt = (props) => {
|
|
17
18
|
const { deviceType, inputDcmt, onClose, fromDTD, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers } = props;
|
|
18
|
-
const {
|
|
19
|
+
const { downloadDcmtsAsync } = useDcmtOperations();
|
|
19
20
|
const [dcmtHistory, setDcmtHistory] = useState([]);
|
|
20
21
|
// showId is a state variable that determines whether an ID-related component or feature should be displayed
|
|
21
22
|
const [showId, setShowId] = useState(false);
|
|
@@ -33,18 +34,6 @@ const TMViewHistoryDcmt = (props) => {
|
|
|
33
34
|
const [showDcmtForm, setShowDcmtForm] = useState(false);
|
|
34
35
|
// Memoized value to get the selected draft based on the focused row key
|
|
35
36
|
const selectedDcmt = useMemo(() => dcmtHistory.find(dcmt => dcmt.id === focusedRowKey), [focusedRowKey, dcmtHistory]);
|
|
36
|
-
// State variable to control the visibility of the wait panel.
|
|
37
|
-
const [showLocalWaitPanel, setShowLocalWaitPanel] = useState(false);
|
|
38
|
-
// State variable to store the title of the wait panel.
|
|
39
|
-
const [waitLocalPanelTitle, setWaitLocalPanelTitle] = useState('');
|
|
40
|
-
// State variable to control the visibility of the primary section of the wait panel.
|
|
41
|
-
const [showLocalPrimary, setShowLocalPrimary] = useState(false);
|
|
42
|
-
// State variable to store the primary text of the wait panel.
|
|
43
|
-
const [waitLocalPanelTextPrimary, setWaitLocalPanelTextPrimary] = useState('');
|
|
44
|
-
// State variable to track the current value of the primary progress indicator in the wait panel.
|
|
45
|
-
const [waitLocalPanelValuePrimary, setWaitLocalPanelValuePrimary] = useState(0);
|
|
46
|
-
// State variable to define the maximum value for the primary progress indicator in the wait panel.
|
|
47
|
-
const [waitLocalPanelMaxValuePrimary, setWaitLocalPanelMaxValuePrimary] = useState(0);
|
|
48
37
|
const getParticipantById = (id, users) => {
|
|
49
38
|
if (users.length === 0)
|
|
50
39
|
return null;
|
|
@@ -155,18 +144,21 @@ const TMViewHistoryDcmt = (props) => {
|
|
|
155
144
|
onButtonClick: async (e) => {
|
|
156
145
|
if (e !== ButtonNames.YES)
|
|
157
146
|
return;
|
|
158
|
-
setWaitLocalPanelTitle(SDKUI_Localizator.Delete);
|
|
159
|
-
setShowLocalWaitPanel(true);
|
|
160
|
-
setShowLocalPrimary(true);
|
|
161
147
|
abortLocalController = new AbortController();
|
|
148
|
+
const panel = TMWaitPanelManager.show({
|
|
149
|
+
title: SDKUI_Localizator.Delete,
|
|
150
|
+
max: 1,
|
|
151
|
+
showCounters: false,
|
|
152
|
+
cancelable: true,
|
|
153
|
+
onAbort: () => abortLocalController.abort(),
|
|
154
|
+
});
|
|
162
155
|
let result = [];
|
|
163
156
|
let i = 0;
|
|
164
|
-
setWaitLocalPanelMaxValuePrimary(1);
|
|
165
157
|
if (abortLocalController.signal.aborted) {
|
|
166
158
|
result.push({ rowIndex: i, id1: currentDcmt.id, id2: currentDcmt.id, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborate ${i}` });
|
|
167
159
|
}
|
|
168
160
|
else {
|
|
169
|
-
|
|
161
|
+
panel.setSubtitle(SDKUI_Localizator.Delete);
|
|
170
162
|
const deleteEngine = new DeleteEngine(SDK_Globals.tmSession);
|
|
171
163
|
await deleteEngine.PhysDeleteByIDAsync(Number(currentDcmt.TID), Number(currentDcmt.DID), false)
|
|
172
164
|
.then(async () => {
|
|
@@ -178,10 +170,7 @@ const TMViewHistoryDcmt = (props) => {
|
|
|
178
170
|
});
|
|
179
171
|
}
|
|
180
172
|
setFocusedRowKey(undefined);
|
|
181
|
-
|
|
182
|
-
setWaitLocalPanelMaxValuePrimary(0);
|
|
183
|
-
setWaitLocalPanelValuePrimary(0);
|
|
184
|
-
setShowLocalWaitPanel(false);
|
|
173
|
+
panel.close();
|
|
185
174
|
TMResultManager.show(result, SDKUI_Localizator.Delete, "ID", undefined);
|
|
186
175
|
}
|
|
187
176
|
});
|
|
@@ -279,7 +268,7 @@ const TMViewHistoryDcmt = (props) => {
|
|
|
279
268
|
{ dataField: "LastUpdateTime", caption: SDKUI_Localizator.LastUpdateTime, dataType: 'datetime', format: 'dd/MM/yyyy HH:mm:ss', },
|
|
280
269
|
]);
|
|
281
270
|
}, [showId]);
|
|
282
|
-
return _jsx(TMModal, { title: `${SDKUI_Localizator.SearchResult} \u2014 ${SDKUI_Localizator.History + ": " + (fromDTD.nameLoc ?? SDKUI_Localizator.Document) + " (DID:" + inputDcmt.DID})`, width: calcResponsiveSizes(deviceType, '700px', '700px', '95%'), height: calcResponsiveSizes(deviceType, '80%', '80%', '95%'), onClose: onClose, children:
|
|
283
|
-
|
|
271
|
+
return _jsx(TMModal, { title: `${SDKUI_Localizator.SearchResult} \u2014 ${SDKUI_Localizator.History + ": " + (fromDTD.nameLoc ?? SDKUI_Localizator.Document) + " (DID:" + inputDcmt.DID})`, width: calcResponsiveSizes(deviceType, '700px', '700px', '95%'), height: calcResponsiveSizes(deviceType, '80%', '80%', '95%'), onClose: onClose, children: _jsxs(TMLayoutContainer, { direction: 'vertical', children: [_jsx(TMDataGrid, { dataSource: dcmtHistory, dataColumns: dataColumns, focusedRowKey: focusedRowKey, selection: selection, customContextMenuItems: customContextMenuItems, onFocusedRowChanged: onFocusedRowChanged, onCellDblClick: onCellDblClick, noDataText: SDKUI_Localizator.NoDataToDisplay, showSearchPanel: showSearch }), (showDcmtForm && selectedDcmt !== undefined) &&
|
|
272
|
+
_jsx(TMDcmtForm, { TID: Number(selectedDcmt.TID), DID: Number(selectedDcmt.DID), formMode: FormModes.ReadOnly, isModal: true, widthModal: "95%", heightModal: "95%", titleModal: fromDTD.name ?? SDKUI_Localizator.SearchResult, allowNavigation: dcmtHistory.length > 0, itemIndex: selectedIndex + 1, count: dcmtHistory.length, onClose: () => { setShowDcmtForm(false); }, canNext: canNavigateHandler('next'), canPrev: canNavigateHandler('prev'), onNext: () => onNavigateHandler('next'), onPrev: () => onNavigateHandler('prev'), allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, moreInfoTasks: getMoreInfoTasksForDocument(allTasks ?? [], Number(selectedDcmt.TID), Number(selectedDcmt.DID)) })] }) });
|
|
284
273
|
};
|
|
285
274
|
export default TMViewHistoryDcmt;
|