@topconsultnpm/sdkui-react 6.22.0-dev2.2 → 6.22.0-dev2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
- package/lib/components/base/TMButton.d.ts +13 -0
- package/lib/components/base/TMButton.js +137 -5
- package/lib/components/base/TMDataGrid.d.ts +2 -0
- package/lib/components/base/TMDataGrid.js +68 -4
- package/lib/components/base/TMEditorBase.d.ts +2 -0
- package/lib/components/base/TMModal.d.ts +4 -0
- package/lib/components/base/TMModal.js +42 -11
- package/lib/components/base/TMPanel.d.ts +1 -0
- package/lib/components/base/TMPanel.js +79 -20
- package/lib/components/base/TMTooltip.d.ts +2 -1
- package/lib/components/base/TMTooltip.js +23 -6
- package/lib/components/base/TMTreeView.js +108 -67
- package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
- package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
- package/lib/components/choosers/TMDistinctValues.js +149 -142
- package/lib/components/choosers/TMMetadataChooser.js +1 -1
- package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
- package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
- package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
- package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
- package/lib/components/editors/TMDropDown.d.ts +3 -0
- package/lib/components/editors/TMDropDown.js +197 -5
- package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
- package/lib/components/editors/TMMetadataEditor.js +8 -3
- package/lib/components/editors/TMTextBox.js +15 -15
- package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
- package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
- package/lib/components/features/documents/TMDcmtForm.js +104 -42
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
- package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
- package/lib/components/features/documents/TMMasterInfoFields.js +172 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +277 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +354 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +234 -0
- package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
- package/lib/components/features/documents/TMRelationViewer.js +69 -20
- package/lib/components/features/search/TMSearch.js +29 -16
- package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
- package/lib/components/features/search/TMSearchQueryPanel.js +28 -7
- package/lib/components/features/search/TMSearchResult.d.ts +9 -0
- package/lib/components/features/search/TMSearchResult.js +323 -92
- package/lib/components/features/search/TMTreeSelector.js +18 -5
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
- package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
- package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
- package/lib/components/layout/panelManager/types.d.ts +3 -0
- package/lib/components/viewers/TMMidViewer.js +4 -1
- package/lib/components/viewers/TMTidViewer.js +25 -11
- package/lib/helper/SDKUI_Globals.d.ts +55 -0
- package/lib/helper/SDKUI_Globals.js +82 -0
- package/lib/helper/SDKUI_Localizator.d.ts +24 -0
- package/lib/helper/SDKUI_Localizator.js +240 -0
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +3 -0
- package/lib/helper/dataGridSearchHelper.d.ts +28 -0
- package/lib/helper/dataGridSearchHelper.js +51 -0
- package/lib/helper/dcmtsHelper.d.ts +20 -0
- package/lib/helper/dcmtsHelper.js +58 -1
- package/lib/helper/helpers.d.ts +20 -0
- package/lib/helper/helpers.js +40 -0
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.d.ts +2 -0
- package/lib/helper/queryHelper.js +3 -1
- package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
- package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
- package/lib/hooks/useArchiveListForm.d.ts +39 -0
- package/lib/hooks/useArchiveListForm.js +46 -0
- package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
- package/lib/hooks/useCaseFlowApprove.js +157 -0
- package/lib/hooks/useDataUserIdItem.d.ts +1 -0
- package/lib/hooks/useDataUserIdItem.js +1 -0
- package/lib/hooks/useDocumentOperations.d.ts +2 -0
- package/lib/hooks/useDocumentOperations.js +28 -5
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +98 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +567 -0
- package/lib/hooks/useRelatedDocuments.js +2 -26
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
- package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
- package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
- package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
- package/lib/hooks/useTMDistinctValuesSource.js +105 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
- package/lib/services/caseflow/CaseFlowService.js +693 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
- package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
- package/lib/services/caseflow/caseFlowNameCache.js +116 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/index.js +4 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +66 -66
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { ArchiveConstraints, ArchiveEngineByID, MetadataDataDomains, MetadataDataTypes, RelationCacheService, RelationTypes, ResultTypes, SDK_Globals } from '@topconsultnpm/sdk-ts';
|
|
3
|
+
import { MetadataValueDescriptorEx } from '../ts';
|
|
4
|
+
import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
|
|
5
|
+
import { getDcmtMetadataByMid, getUserMetadataSorted, mapAssociationsFromMetadata } from '../helper/dcmtsHelper';
|
|
6
|
+
import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../components/base/TMPopUp';
|
|
7
|
+
import TMSpinner from '../components/base/TMSpinner';
|
|
8
|
+
import ShowAlert from '../components/base/TMAlert';
|
|
9
|
+
import { TMResultManager } from '../components/forms/TMResultDialog';
|
|
10
|
+
import { useDataListItem } from './useDataListItem';
|
|
11
|
+
import { buildColumnsFromDtd, canArchive, canUpdateMasterCallback, getDetailDtdAsync } from '../components/features/documents/TMMultiMasterDetailDcmtsUtils';
|
|
12
|
+
/** Fasi del flusso di archiviazione multipla dei dettagli */
|
|
13
|
+
export var MultiDetailArchivePhase;
|
|
14
|
+
(function (MultiDetailArchivePhase) {
|
|
15
|
+
/** Scelta del tipo documento */
|
|
16
|
+
MultiDetailArchivePhase[MultiDetailArchivePhase["SelectDcmtType"] = 0] = "SelectDcmtType";
|
|
17
|
+
/** Riepilogo delle archiviazioni */
|
|
18
|
+
MultiDetailArchivePhase[MultiDetailArchivePhase["ArchiveSummary"] = 1] = "ArchiveSummary";
|
|
19
|
+
})(MultiDetailArchivePhase || (MultiDetailArchivePhase = {}));
|
|
20
|
+
const CLOSED_WAIT_PANEL = {
|
|
21
|
+
showWaitPanel: false,
|
|
22
|
+
waitPanelTitle: '',
|
|
23
|
+
showWaitPanelPrimary: false,
|
|
24
|
+
waitPanelTextPrimary: '',
|
|
25
|
+
waitPanelValuePrimary: 0,
|
|
26
|
+
waitPanelMaxValuePrimary: 0,
|
|
27
|
+
showWaitPanelSecondary: false,
|
|
28
|
+
waitPanelTextSecondary: '',
|
|
29
|
+
waitPanelValueSecondary: 0,
|
|
30
|
+
waitPanelMaxValueSecondary: 0,
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Logica dell'archiviazione multipla dei dettagli: relazioni, sezioni, righe del riepilogo e
|
|
34
|
+
* archiviazione. Il rendering sta in TMMultiMasterDetailDcmtsForm
|
|
35
|
+
*/
|
|
36
|
+
export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
37
|
+
const { renderDataListCell, loadDataListsAsync } = useDataListItem();
|
|
38
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
39
|
+
const [phase, setPhase] = useState(MultiDetailArchivePhase.SelectDcmtType);
|
|
40
|
+
const [detailRelationItems, setDetailRelationItems] = useState([]);
|
|
41
|
+
const [selectedRelation, setSelectedRelation] = useState(undefined);
|
|
42
|
+
// Il mount della griglia è differito di un frame, così stepper e titolo si dipingono subito
|
|
43
|
+
const [isSummaryReady, setIsSummaryReady] = useState(false);
|
|
44
|
+
// Struttura del tipo documento selezionato
|
|
45
|
+
const [detailColumns, setDetailColumns] = useState([]);
|
|
46
|
+
const [detailSupportsFile, setDetailSupportsFile] = useState(false);
|
|
47
|
+
// Dettagli già archiviati del tipo selezionato, collegati al master
|
|
48
|
+
const [archivedDetailResults, setArchivedDetailResults] = useState([]);
|
|
49
|
+
const [isLoadingArchivedDetails, setIsLoadingArchivedDetails] = useState(false);
|
|
50
|
+
const areArchivedDetailsLoadedRef = useRef(false);
|
|
51
|
+
// Sezioni aperte/collassate, come le ha lasciate l'utente
|
|
52
|
+
const [sectionsOpen, setSectionsOpen] = useState(getMultiDetailSectionsOpen());
|
|
53
|
+
// Cambia a ogni cancellazione del layout, per rimontare i pannelli sulle dimensioni di default
|
|
54
|
+
const [layoutResetToken, setLayoutResetToken] = useState(0);
|
|
55
|
+
// Master
|
|
56
|
+
const [isOpenMasterForm, setIsOpenMasterForm] = useState(false);
|
|
57
|
+
const [masterInfoReloadToken, setMasterInfoReloadToken] = useState(0);
|
|
58
|
+
// Righe del riepilogo
|
|
59
|
+
const [archiveRows, setArchiveRows] = useState([]);
|
|
60
|
+
const rowIdRef = useRef(0);
|
|
61
|
+
const [focusedRowKey, setFocusedRowKey] = useState(undefined);
|
|
62
|
+
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
63
|
+
// Form di inserimento/modifica di una riga
|
|
64
|
+
const [isOpenAddForm, setIsOpenAddForm] = useState(false);
|
|
65
|
+
const [editingRowId, setEditingRowId] = useState(undefined); // Undefined = inserimento
|
|
66
|
+
const [associationInputMids, setAssociationInputMids] = useState([]); // Associazioni pre-compilate dal master
|
|
67
|
+
// Archiviazione finale
|
|
68
|
+
const [stopOnFirstError, setStopOnFirstError] = useState(true);
|
|
69
|
+
const [isArchiving, setIsArchiving] = useState(false);
|
|
70
|
+
const [waitPanel, setWaitPanel] = useState(CLOSED_WAIT_PANEL);
|
|
71
|
+
const abortControllerRef = useRef(undefined);
|
|
72
|
+
const selectedItem = detailRelationItems.find(item => item.relation === selectedRelation);
|
|
73
|
+
const hasMultipleTypes = detailRelationItems.length > 1;
|
|
74
|
+
const canUpdateMaster = canUpdateMasterCallback(dtd, masterDcmt);
|
|
75
|
+
const masterFieldsCount = useMemo(() => getUserMetadataSorted(dtd).length, [dtd]);
|
|
76
|
+
// Nel riepilogo si mostrano solo le righe del tipo selezionato
|
|
77
|
+
const currentRows = useMemo(() => archiveRows.filter(r => r.__tid === selectedRelation?.detailTID), [archiveRows, selectedRelation?.detailTID]);
|
|
78
|
+
const archivedDetailsCount = archivedDetailResults.reduce((total, r) => total + (r.dcmtsFound ?? r.jsonResult?.length ?? 0), 0);
|
|
79
|
+
// =============================================================================================
|
|
80
|
+
// MASTER
|
|
81
|
+
// =============================================================================================
|
|
82
|
+
// Metadati del master indicizzati per mid: il master è fisso, quindi si legge una sola volta
|
|
83
|
+
// e la cache si invalida solo se cambia il documento (TID/DID) o dopo un salvataggio
|
|
84
|
+
const masterMetadataCacheRef = useRef(undefined);
|
|
85
|
+
const getMasterMetadataAsync = () => {
|
|
86
|
+
const key = `${masterDcmt?.TID}_${masterDcmt?.DID}`;
|
|
87
|
+
if (masterMetadataCacheRef.current?.key !== key) {
|
|
88
|
+
masterMetadataCacheRef.current = { key, promise: getDcmtMetadataByMid(masterDcmt) };
|
|
89
|
+
}
|
|
90
|
+
return masterMetadataCacheRef.current.promise;
|
|
91
|
+
};
|
|
92
|
+
const handleMasterSavedAsync = async () => {
|
|
93
|
+
masterMetadataCacheRef.current = undefined;
|
|
94
|
+
setMasterInfoReloadToken(token => token + 1);
|
|
95
|
+
setIsOpenMasterForm(false);
|
|
96
|
+
};
|
|
97
|
+
// Associazioni della relazione valorizzate dal master, per pre-compilare il dettaglio
|
|
98
|
+
const getMasterAssociationsForDetailAsync = async (relation) => {
|
|
99
|
+
if (!relation)
|
|
100
|
+
return [];
|
|
101
|
+
return mapAssociationsFromMetadata(relation, 'detail', await getMasterMetadataAsync());
|
|
102
|
+
};
|
|
103
|
+
// =============================================================================================
|
|
104
|
+
// RELAZIONI E STRUTTURA DEL DETTAGLIO
|
|
105
|
+
// =============================================================================================
|
|
106
|
+
// Relazioni di dettaglio archiviabili; con una sola relazione si salta la scelta del tipo
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
const loadAsync = async () => {
|
|
109
|
+
try {
|
|
110
|
+
setIsLoading(true);
|
|
111
|
+
TMSpinner.show({ description: SDKUI_Localizator.Loading });
|
|
112
|
+
const relations = await RelationCacheService.GetAllAsync(SDK_Globals.tmSession);
|
|
113
|
+
if (!relations)
|
|
114
|
+
throw new Error('Impossibile caricare le relazioni.');
|
|
115
|
+
// Relazioni in cui il tipo corrente è master: i target sono i dettagli archiviabili
|
|
116
|
+
const detailRelations = relations.filter(r => r.masterTID === dtd?.id
|
|
117
|
+
&& r.relationType !== RelationTypes.ManyToMany
|
|
118
|
+
&& r.associations
|
|
119
|
+
&& r.associations.length > 0);
|
|
120
|
+
const items = await Promise.all(detailRelations.map(async (relation) => ({
|
|
121
|
+
relation,
|
|
122
|
+
detailDtd: await getDetailDtdAsync(relation.detailTID),
|
|
123
|
+
})));
|
|
124
|
+
setDetailRelationItems(items);
|
|
125
|
+
if (items.length === 1) {
|
|
126
|
+
setSelectedRelation(items[0].relation);
|
|
127
|
+
setPhase(MultiDetailArchivePhase.ArchiveSummary);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
setPhase(MultiDetailArchivePhase.SelectDcmtType);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
TMExceptionBoxManager.show({ exception: error });
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
TMSpinner.hide();
|
|
138
|
+
setIsLoading(false);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
loadAsync();
|
|
142
|
+
}, []);
|
|
143
|
+
// Colonne della griglia di riepilogo e supporto al file del tipo selezionato
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const loadDetailStructureAsync = async () => {
|
|
146
|
+
if (!selectedRelation?.detailTID) {
|
|
147
|
+
setDetailColumns([]);
|
|
148
|
+
setDetailSupportsFile(false);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const detailDtd = await getDetailDtdAsync(selectedRelation.detailTID);
|
|
153
|
+
setDetailSupportsFile(detailDtd?.archiveConstraint !== ArchiveConstraints.OnlyMetadata);
|
|
154
|
+
// Liste dati referenziate dai metadati, servono al render delle celle
|
|
155
|
+
const dataListIDs = new Set();
|
|
156
|
+
(detailDtd?.metadata ?? []).forEach(md => {
|
|
157
|
+
if (md.dataDomain === MetadataDataDomains.DataList && md.dataListID)
|
|
158
|
+
dataListIDs.add(md.dataListID);
|
|
159
|
+
});
|
|
160
|
+
await loadDataListsAsync(dataListIDs);
|
|
161
|
+
setDetailColumns(buildColumnsFromDtd(detailDtd, renderDataListCell));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
setDetailColumns([]);
|
|
165
|
+
setDetailSupportsFile(false);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
loadDetailStructureAsync();
|
|
169
|
+
}, [selectedRelation?.detailTID]);
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
if (phase !== MultiDetailArchivePhase.ArchiveSummary) {
|
|
172
|
+
setIsSummaryReady(false);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
setIsSummaryReady(false);
|
|
176
|
+
const rafId = requestAnimationFrame(() => setIsSummaryReady(true));
|
|
177
|
+
return () => cancelAnimationFrame(rafId);
|
|
178
|
+
}, [phase, selectedRelation?.detailTID]);
|
|
179
|
+
// =============================================================================================
|
|
180
|
+
// DETTAGLI GIÀ ARCHIVIATI
|
|
181
|
+
// =============================================================================================
|
|
182
|
+
const refreshArchivedDetailsAsync = async () => {
|
|
183
|
+
const detailTID = selectedRelation?.detailTID;
|
|
184
|
+
if (detailTID === undefined || !masterDcmt?.TID || !masterDcmt?.DID)
|
|
185
|
+
return;
|
|
186
|
+
try {
|
|
187
|
+
setIsLoadingArchivedDetails(true);
|
|
188
|
+
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
189
|
+
const results = await searchEngine?.GetAllDetailDcmtsAsync(masterDcmt.TID, masterDcmt.DID) ?? [];
|
|
190
|
+
let filtered = results.filter(r => selectedRelation?.id !== undefined && r.relationID === selectedRelation.id);
|
|
191
|
+
if (filtered.length === 0)
|
|
192
|
+
filtered = results.filter(r => r.fromTID === detailTID);
|
|
193
|
+
setArchivedDetailResults(filtered);
|
|
194
|
+
areArchivedDetailsLoadedRef.current = true;
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
TMExceptionBoxManager.show({ exception: error });
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
setIsLoadingArchivedDetails(false);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
// I risultati seguono il tipo documento selezionato
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
setArchivedDetailResults([]);
|
|
206
|
+
areArchivedDetailsLoadedRef.current = false;
|
|
207
|
+
if (selectedRelation?.detailTID !== undefined && sectionsOpen.archivedDetails)
|
|
208
|
+
refreshArchivedDetailsAsync();
|
|
209
|
+
}, [selectedRelation?.detailTID]);
|
|
210
|
+
// =============================================================================================
|
|
211
|
+
// SEZIONI
|
|
212
|
+
// =============================================================================================
|
|
213
|
+
const setSectionOpen = (section, isOpen) => {
|
|
214
|
+
const next = { ...sectionsOpen, [section]: isOpen };
|
|
215
|
+
setSectionsOpen(next);
|
|
216
|
+
saveMultiDetailSectionsOpen(next);
|
|
217
|
+
// I dettagli già archiviati si caricano alla prima apertura della sezione
|
|
218
|
+
if (section === 'archivedDetails' && isOpen && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
219
|
+
refreshArchivedDetailsAsync();
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Apre tutte le sezioni senza salvare: serve dove il collasso non è una scelta dell'utente ma una
|
|
223
|
+
* conseguenza del layout (su mobile si vede un pannello alla volta, e una sezione chiusa lascerebbe
|
|
224
|
+
* a schermo la sola testata). Non si persiste per non sovrascrivere le sezioni che l'utente ha
|
|
225
|
+
* chiuso nella vista a pannelli impilati.
|
|
226
|
+
*/
|
|
227
|
+
const openAllSections = () => {
|
|
228
|
+
setSectionsOpen(prev => Object.values(prev).every(Boolean) ? prev : { masterInfo: true, archivedDetails: true, archiveList: true });
|
|
229
|
+
// I dettagli già archiviati si caricano come alla prima apertura della sezione
|
|
230
|
+
if (!areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
231
|
+
refreshArchivedDetailsAsync();
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Cancella il layout configurato dall'utente (altezze dei pannelli e sezioni collassate), previa
|
|
235
|
+
* conferma. Il token cambia per rimontare il panel manager, che legge le dimensioni solo all'avvio.
|
|
236
|
+
*/
|
|
237
|
+
const clearLayout = () => {
|
|
238
|
+
TMMessageBoxManager.show({
|
|
239
|
+
title: 'Cancella layout',
|
|
240
|
+
message: 'Vuoi cancellare il layout?',
|
|
241
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
242
|
+
onButtonClick: (button) => {
|
|
243
|
+
if (button !== ButtonNames.YES)
|
|
244
|
+
return;
|
|
245
|
+
clearMultiDetailLayout();
|
|
246
|
+
const restored = getMultiDetailSectionsOpen();
|
|
247
|
+
setSectionsOpen(restored);
|
|
248
|
+
setLayoutResetToken(prev => prev + 1);
|
|
249
|
+
// Le sezioni riaperte dal ripristino caricano i dettagli già archiviati come alla prima apertura
|
|
250
|
+
if (restored.archivedDetails && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
251
|
+
refreshArchivedDetailsAsync();
|
|
252
|
+
ShowAlert({ message: 'Layout cancellato', mode: 'success', title: 'Cancella layout', duration: 3000 });
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
// =============================================================================================
|
|
257
|
+
// NAVIGAZIONE
|
|
258
|
+
// =============================================================================================
|
|
259
|
+
const selectRelation = (item) => {
|
|
260
|
+
setSelectedRelation(item.relation);
|
|
261
|
+
setPhase(MultiDetailArchivePhase.ArchiveSummary);
|
|
262
|
+
};
|
|
263
|
+
const backToTypeSelection = () => {
|
|
264
|
+
setSelectedRelation(undefined);
|
|
265
|
+
setPhase(MultiDetailArchivePhase.SelectDcmtType);
|
|
266
|
+
};
|
|
267
|
+
// Con righe inserite per il tipo corrente il ritorno alla scelta del tipo è bloccato
|
|
268
|
+
const isBackLocked = currentRows.length > 0;
|
|
269
|
+
const closeModal = () => {
|
|
270
|
+
if (archiveRows.length === 0) {
|
|
271
|
+
onClose();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
TMMessageBoxManager.show({
|
|
275
|
+
title: SDKUI_Localizator.Archive,
|
|
276
|
+
message: SDKUI_Localizator.ExitWithoutArchiving_Confirm,
|
|
277
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
278
|
+
onButtonClick: (button) => { if (button === ButtonNames.YES)
|
|
279
|
+
onClose(); },
|
|
280
|
+
});
|
|
281
|
+
};
|
|
282
|
+
// =============================================================================================
|
|
283
|
+
// RIGHE DEL RIEPILOGO
|
|
284
|
+
// =============================================================================================
|
|
285
|
+
const openAddForm = () => {
|
|
286
|
+
if (!selectedRelation?.detailTID)
|
|
287
|
+
return;
|
|
288
|
+
setEditingRowId(undefined);
|
|
289
|
+
setIsOpenAddForm(true);
|
|
290
|
+
};
|
|
291
|
+
// Identità stabile: la griglia dei dettagli la usa nelle sue callback memoizzate
|
|
292
|
+
const openEditForm = useCallback((row) => {
|
|
293
|
+
if (!row)
|
|
294
|
+
return;
|
|
295
|
+
setEditingRowId(row.id);
|
|
296
|
+
setIsOpenAddForm(true);
|
|
297
|
+
}, []);
|
|
298
|
+
const closeAddForm = () => {
|
|
299
|
+
setEditingRowId(undefined);
|
|
300
|
+
setIsOpenAddForm(false);
|
|
301
|
+
};
|
|
302
|
+
const deleteRow = useCallback((row) => {
|
|
303
|
+
if (!row)
|
|
304
|
+
return;
|
|
305
|
+
TMMessageBoxManager.show({
|
|
306
|
+
title: SDKUI_Localizator.Remove,
|
|
307
|
+
message: SDKUI_Localizator.RemoveDetailFromSummary_Confirm,
|
|
308
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
309
|
+
onButtonClick: (button) => {
|
|
310
|
+
if (button !== ButtonNames.YES)
|
|
311
|
+
return;
|
|
312
|
+
setArchiveRows(prev => prev.filter(r => r.id !== row.id));
|
|
313
|
+
setFocusedRowKey(prev => (prev === row.id ? undefined : prev));
|
|
314
|
+
setSelectedRowKeys(prev => prev.filter(k => k !== row.id));
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
}, []);
|
|
318
|
+
const buildArchiveRow = useCallback((id, tid, values, file) => {
|
|
319
|
+
const row = { id, __tid: tid, __values: values, __file: file };
|
|
320
|
+
for (const v of values) {
|
|
321
|
+
if (v.mid !== undefined)
|
|
322
|
+
row[String(v.mid)] = v.value ?? '';
|
|
323
|
+
}
|
|
324
|
+
return row;
|
|
325
|
+
}, []);
|
|
326
|
+
// Aggiunge/aggiorna nel riepilogo la riga compilata nel form, senza archiviare a sistema
|
|
327
|
+
const upsertRow = (values, file) => {
|
|
328
|
+
const tid = selectedRelation?.detailTID;
|
|
329
|
+
if (tid === undefined)
|
|
330
|
+
return;
|
|
331
|
+
if (editingRowId !== undefined) {
|
|
332
|
+
setArchiveRows(prev => prev.map(r => (r.id === editingRowId ? buildArchiveRow(editingRowId, tid, values, file) : r)));
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
rowIdRef.current += 1;
|
|
336
|
+
setArchiveRows(prev => [...prev, buildArchiveRow(rowIdRef.current, tid, values, file)]);
|
|
337
|
+
}
|
|
338
|
+
closeAddForm();
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Modifica diretta in griglia di uno o più metadati testuali (changes: mid -> valore).
|
|
342
|
+
* La riga e i suoi __values sono ricreati: il duplicato, che condivide __values per
|
|
343
|
+
* riferimento, resta invariato
|
|
344
|
+
*/
|
|
345
|
+
const updateRowValues = useCallback((rowId, changes) => {
|
|
346
|
+
const changedMids = Object.keys(changes);
|
|
347
|
+
if (changedMids.length === 0)
|
|
348
|
+
return;
|
|
349
|
+
// La griglia restituisce null sull'editor svuotato: a sistema il metadato va vuoto, non nullo
|
|
350
|
+
const normalized = {};
|
|
351
|
+
changedMids.forEach(mid => { normalized[mid] = changes[mid] ?? ''; });
|
|
352
|
+
setArchiveRows(prev => prev.map(r => {
|
|
353
|
+
if (r.id !== rowId)
|
|
354
|
+
return r;
|
|
355
|
+
const values = r.__values.map(v => (v.mid !== undefined && normalized[String(v.mid)] !== undefined
|
|
356
|
+
? { ...v, value: normalized[String(v.mid)] }
|
|
357
|
+
: v));
|
|
358
|
+
// Un metadato assente dai valori del form non arriverebbe all'archiviazione
|
|
359
|
+
changedMids.filter(mid => !values.some(v => String(v.mid) === mid)).forEach(mid => {
|
|
360
|
+
const added = new MetadataValueDescriptorEx();
|
|
361
|
+
added.tid = r.__tid;
|
|
362
|
+
added.mid = Number(mid);
|
|
363
|
+
added.value = normalized[mid];
|
|
364
|
+
values.push(added);
|
|
365
|
+
});
|
|
366
|
+
const updated = { ...r, __values: values };
|
|
367
|
+
changedMids.forEach(mid => { updated[mid] = normalized[mid]; });
|
|
368
|
+
return updated;
|
|
369
|
+
}));
|
|
370
|
+
}, []);
|
|
371
|
+
// I __values sono condivisi per riferimento: la modifica sostituisce l'intero array, quindi non intacca il duplicato
|
|
372
|
+
const duplicateRow = useCallback((row) => {
|
|
373
|
+
if (!row)
|
|
374
|
+
return;
|
|
375
|
+
rowIdRef.current += 1;
|
|
376
|
+
setArchiveRows(prev => [...prev, buildArchiveRow(rowIdRef.current, row.__tid, row.__values, row.__file)]);
|
|
377
|
+
}, [buildArchiveRow]);
|
|
378
|
+
// All'apertura del form pre-compila i MID di collegamento valorizzati dal master
|
|
379
|
+
useEffect(() => {
|
|
380
|
+
if (!isOpenAddForm || !selectedRelation) {
|
|
381
|
+
setAssociationInputMids([]);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
let cancelled = false;
|
|
385
|
+
getMasterAssociationsForDetailAsync(selectedRelation)
|
|
386
|
+
.then(associations => { if (!cancelled)
|
|
387
|
+
setAssociationInputMids(associations); })
|
|
388
|
+
.catch(() => { if (!cancelled)
|
|
389
|
+
setAssociationInputMids([]); });
|
|
390
|
+
return () => { cancelled = true; };
|
|
391
|
+
}, [isOpenAddForm, selectedRelation]);
|
|
392
|
+
// MID pre-compilati: in inserimento i collegamenti dal master, in modifica i valori della riga
|
|
393
|
+
const editingRow = editingRowId !== undefined ? archiveRows.find(r => r.id === editingRowId) : undefined;
|
|
394
|
+
const nextFormInputMids = editingRow
|
|
395
|
+
? editingRow.__values
|
|
396
|
+
.filter(v => v.mid !== undefined && v.value !== undefined && v.value !== null && v.value !== '')
|
|
397
|
+
.map(v => ({ mid: v.mid, value: String(v.value) }))
|
|
398
|
+
: associationInputMids;
|
|
399
|
+
// Il form riapplica i valori quando cambia il riferimento di inputMids: si mantiene la stessa
|
|
400
|
+
// istanza finché il contenuto non cambia, così le modifiche dell'utente non vengono sovrascritte
|
|
401
|
+
const formInputMidsSignature = nextFormInputMids.map(o => `${o.mid}=${o.value}`).join('|');
|
|
402
|
+
const formInputMidsRef = useRef(nextFormInputMids);
|
|
403
|
+
const formInputMidsSignatureRef = useRef(formInputMidsSignature);
|
|
404
|
+
if (formInputMidsSignatureRef.current !== formInputMidsSignature) {
|
|
405
|
+
formInputMidsSignatureRef.current = formInputMidsSignature;
|
|
406
|
+
formInputMidsRef.current = nextFormInputMids;
|
|
407
|
+
}
|
|
408
|
+
const formInputMids = formInputMidsRef.current;
|
|
409
|
+
// =============================================================================================
|
|
410
|
+
// ARCHIVIAZIONE
|
|
411
|
+
// =============================================================================================
|
|
412
|
+
// Archivia solo le righe del tipo corrente: quelle di altri tipi restano intatte
|
|
413
|
+
const archiveAllAsync = async () => {
|
|
414
|
+
const detailTID = selectedRelation?.detailTID;
|
|
415
|
+
if (detailTID === undefined)
|
|
416
|
+
return;
|
|
417
|
+
const rowsToArchive = archiveRows.filter(r => r.__tid === detailTID);
|
|
418
|
+
if (rowsToArchive.length === 0) {
|
|
419
|
+
ShowAlert({ message: SDKUI_Localizator.NoDataToDisplay, mode: 'warning', title: SDKUI_Localizator.Archive, duration: 3000 });
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
try {
|
|
423
|
+
setIsArchiving(true);
|
|
424
|
+
const detailDtd = await getDetailDtdAsync(detailTID);
|
|
425
|
+
if (!canArchive(detailDtd)) {
|
|
426
|
+
ShowAlert({ message: SDKUI_Localizator.YouDoNotHavePermissionsToArchiveDetailDocumentsOfThisType, mode: 'warning', title: SDKUI_Localizator.Archive, duration: 5000 });
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
const dataTypeByMid = new Map();
|
|
430
|
+
(detailDtd?.metadata ?? []).forEach(md => { if (md.id !== undefined)
|
|
431
|
+
dataTypeByMid.set(md.id, md.dataType); });
|
|
432
|
+
// Collegamenti della relazione valorizzati dal master (prevalgono sui valori del form)
|
|
433
|
+
const associationMids = await getMasterAssociationsForDetailAsync(selectedRelation);
|
|
434
|
+
// Barra primaria: avanzamento documenti. Barra secondaria: upload del file corrente
|
|
435
|
+
const abortController = new AbortController();
|
|
436
|
+
abortControllerRef.current = abortController;
|
|
437
|
+
setWaitPanel({
|
|
438
|
+
...CLOSED_WAIT_PANEL,
|
|
439
|
+
showWaitPanel: true,
|
|
440
|
+
waitPanelTitle: SDKUI_Localizator.Archiving,
|
|
441
|
+
showWaitPanelPrimary: rowsToArchive.length > 1,
|
|
442
|
+
waitPanelMaxValuePrimary: rowsToArchive.length,
|
|
443
|
+
showWaitPanelSecondary: true,
|
|
444
|
+
});
|
|
445
|
+
const result = [];
|
|
446
|
+
const archivedRowIds = new Set();
|
|
447
|
+
for (let i = 0; i < rowsToArchive.length; i++) {
|
|
448
|
+
if (abortController.signal.aborted) {
|
|
449
|
+
result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
const rowData = rowsToArchive[i];
|
|
453
|
+
try {
|
|
454
|
+
setWaitPanel(prev => ({ ...prev, waitPanelTextPrimary: `${SDKUI_Localizator.Archiving} ${i + 1} / ${rowsToArchive.length}` }));
|
|
455
|
+
const newDID = await archiveRowAsync(rowData, detailTID, dataTypeByMid, associationMids, abortController.signal);
|
|
456
|
+
setWaitPanel(prev => ({ ...prev, waitPanelValuePrimary: i + 1 }));
|
|
457
|
+
archivedRowIds.add(rowData.id);
|
|
458
|
+
result.push({ rowIndex: i, id1: detailTID, id2: newDID, resultType: ResultTypes.SUCCESS });
|
|
459
|
+
}
|
|
460
|
+
catch (error) {
|
|
461
|
+
const err = error;
|
|
462
|
+
// Annullamento richiesto dall'utente: interrompe senza mostrare l'errore
|
|
463
|
+
if (err?.name === 'CanceledError' || abortController.signal.aborted) {
|
|
464
|
+
result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${i} documenti` });
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
result.push({ rowIndex: i, id1: detailTID, id2: undefined, resultType: ResultTypes.ERROR, description: getExceptionMessage(error) });
|
|
468
|
+
if (stopOnFirstError)
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
// Le righe archiviate escono dal riepilogo
|
|
473
|
+
if (archivedRowIds.size > 0) {
|
|
474
|
+
setArchiveRows(prev => prev.filter(r => !archivedRowIds.has(r.id)));
|
|
475
|
+
setSelectedRowKeys([]);
|
|
476
|
+
setFocusedRowKey(undefined);
|
|
477
|
+
if (areArchivedDetailsLoadedRef.current)
|
|
478
|
+
refreshArchivedDetailsAsync();
|
|
479
|
+
}
|
|
480
|
+
const successCount = result.filter(o => o.resultType === ResultTypes.SUCCESS).length;
|
|
481
|
+
const successMsg = `${successCount} ${successCount === 1 ? 'documento di dettaglio archiviato' : 'documenti di dettaglio archiviati'} con successo`;
|
|
482
|
+
TMResultManager.show(result, SDKUI_Localizator.Archive, 'TID', 'DID', successMsg);
|
|
483
|
+
// Tutte le righe del tipo corrente archiviate senza errori: il modale si chiude
|
|
484
|
+
const errorCount = result.filter(o => o.resultType === ResultTypes.ERROR).length;
|
|
485
|
+
if (errorCount === 0 && archivedRowIds.size === rowsToArchive.length)
|
|
486
|
+
onClose();
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
TMExceptionBoxManager.show({ exception: error });
|
|
490
|
+
}
|
|
491
|
+
finally {
|
|
492
|
+
abortControllerRef.current = undefined;
|
|
493
|
+
setWaitPanel(CLOSED_WAIT_PANEL);
|
|
494
|
+
setIsArchiving(false);
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
// Archiviazione di una singola riga, con avanzamento dell'upload sulla barra secondaria
|
|
498
|
+
const archiveRowAsync = async (row, detailTID, dataTypeByMid, associationMids, signal) => {
|
|
499
|
+
const ae = new ArchiveEngineByID(SDK_Globals.tmSession);
|
|
500
|
+
ae.TID = detailTID;
|
|
501
|
+
ae.Metadata_ClearAll();
|
|
502
|
+
// Valori del form + associazioni con il master (le associazioni prevalgono)
|
|
503
|
+
const valueByMid = new Map();
|
|
504
|
+
for (const v of row.__values) {
|
|
505
|
+
if (v.mid !== undefined && v.value)
|
|
506
|
+
valueByMid.set(v.mid, v.value);
|
|
507
|
+
}
|
|
508
|
+
for (const a of associationMids) {
|
|
509
|
+
if (a.value)
|
|
510
|
+
valueByMid.set(a.mid, a.value);
|
|
511
|
+
}
|
|
512
|
+
valueByMid.forEach((value, mid) => {
|
|
513
|
+
switch (dataTypeByMid.get(mid)) {
|
|
514
|
+
case MetadataDataTypes.DateTime:
|
|
515
|
+
ae.Metadata_AddDateTime(mid, new Date(value));
|
|
516
|
+
break;
|
|
517
|
+
case MetadataDataTypes.Number:
|
|
518
|
+
ae.Metadata_AddNumber(mid, parseFloat(value));
|
|
519
|
+
break;
|
|
520
|
+
default:
|
|
521
|
+
ae.Metadata_AddString(mid, value);
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
if (row.__file)
|
|
526
|
+
ae.ArchivingFile = row.__file;
|
|
527
|
+
let firstBlock = true;
|
|
528
|
+
let maxFileSize = 0;
|
|
529
|
+
return ae.ArchiveAsync(signal, (pd) => {
|
|
530
|
+
if (firstBlock) {
|
|
531
|
+
maxFileSize = pd.ProgressBarMaximum ?? 0;
|
|
532
|
+
setWaitPanel(prev => ({ ...prev, waitPanelMaxValueSecondary: maxFileSize }));
|
|
533
|
+
firstBlock = false;
|
|
534
|
+
}
|
|
535
|
+
setWaitPanel(prev => ({
|
|
536
|
+
...prev,
|
|
537
|
+
waitPanelValueSecondary: pd.ProgressBarValue,
|
|
538
|
+
waitPanelTextSecondary: `Uploading... ${Globalization.getNumberDisplayValue(pd.ProgressBarValue, true)} / ${Globalization.getNumberDisplayValue(maxFileSize, true)}`,
|
|
539
|
+
}));
|
|
540
|
+
if (pd.ProgressBarValue === pd.ProgressBarMaximum) {
|
|
541
|
+
setWaitPanel(prev => ({ ...prev, waitPanelValueSecondary: 0, waitPanelMaxValueSecondary: 0, waitPanelTextSecondary: '' }));
|
|
542
|
+
firstBlock = true;
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
};
|
|
546
|
+
return {
|
|
547
|
+
// Flusso
|
|
548
|
+
isLoading, isSummaryReady, phase, setPhase,
|
|
549
|
+
detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes,
|
|
550
|
+
selectRelation, backToTypeSelection, isBackLocked, closeModal,
|
|
551
|
+
// Sezioni
|
|
552
|
+
sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken,
|
|
553
|
+
// Master
|
|
554
|
+
canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken,
|
|
555
|
+
isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync,
|
|
556
|
+
// Dettagli già archiviati
|
|
557
|
+
archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync,
|
|
558
|
+
// Dettagli da archiviare
|
|
559
|
+
detailColumns, detailSupportsFile, archiveRows, currentRows,
|
|
560
|
+
focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys,
|
|
561
|
+
isOpenAddForm, editingRowId, editingRow, formInputMids,
|
|
562
|
+
openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues,
|
|
563
|
+
// Archiviazione
|
|
564
|
+
stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync,
|
|
565
|
+
waitPanel, abortController: abortControllerRef.current,
|
|
566
|
+
};
|
|
567
|
+
};
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { useState, useCallback, useMemo, useEffect } from 'react';
|
|
3
3
|
import { SDKUI_Localizator } from '../helper/SDKUI_Localizator';
|
|
4
4
|
import { IconPair, IconUnpair } from '../helper/TMIcons';
|
|
5
|
-
import { hasDetailRelations, hasMasterRelations } from '../helper/dcmtsHelper';
|
|
5
|
+
import { hasDetailRelations, hasMasterRelations, mapRelationAssociationsToMids } from '../helper/dcmtsHelper';
|
|
6
6
|
import { SDK_Globals, SearchEngine, RelationCacheService, RelationTypes, DcmtTypeListCacheService, SystemMIDsAsNumber, AccessLevelsEx } from '@topconsultnpm/sdk-ts';
|
|
7
7
|
import { TMSpinner, TMExceptionBoxManager, ShowAlert } from '../components';
|
|
8
8
|
export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, currentSearchResults }) => {
|
|
@@ -224,31 +224,7 @@ export const useRelatedDocuments = ({ selectedSearchResult, focusedItem, current
|
|
|
224
224
|
};
|
|
225
225
|
//This ensures archive master/detail works correctly regardless of output metadata configuration.
|
|
226
226
|
const mapAssociationsToMidsAsync = useCallback(async (relation, type) => {
|
|
227
|
-
|
|
228
|
-
return [];
|
|
229
|
-
if (!focusedItem?.TID || !focusedItem?.DID)
|
|
230
|
-
return [];
|
|
231
|
-
const searchEngine = SDK_Globals.tmSession?.NewSearchEngine();
|
|
232
|
-
if (!searchEngine)
|
|
233
|
-
return [];
|
|
234
|
-
const allMetadata = await searchEngine.GetMetadataAsync(focusedItem.TID, focusedItem.DID, false);
|
|
235
|
-
if (!allMetadata?.dtdResult?.rows?.[0] || !allMetadata.selectMIDs)
|
|
236
|
-
return [];
|
|
237
|
-
const row = allMetadata.dtdResult.rows[0];
|
|
238
|
-
const mids = allMetadata.selectMIDs;
|
|
239
|
-
const metadataMap = new Map();
|
|
240
|
-
mids.forEach((mid, index) => {
|
|
241
|
-
const value = row[index];
|
|
242
|
-
if (value !== undefined && value !== null) {
|
|
243
|
-
metadataMap.set(mid, value.toString());
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
return relation.associations.map(assoc => {
|
|
247
|
-
const sourceMid = type === 'detail' ? (assoc.item1 ?? 0) : (assoc.item2 ?? 0);
|
|
248
|
-
const targetMid = type === 'detail' ? (assoc.item2 ?? 0) : (assoc.item1 ?? 0);
|
|
249
|
-
const value = metadataMap.get(sourceMid) ?? '';
|
|
250
|
-
return { mid: targetMid, value };
|
|
251
|
-
});
|
|
227
|
+
return mapRelationAssociationsToMids(relation, type, focusedItem);
|
|
252
228
|
}, [focusedItem]);
|
|
253
229
|
const archiveRelatedDcmtHandler = useCallback(async (relation, type) => {
|
|
254
230
|
const targetTID = type === 'detail' ? relation.detailTID : relation.masterTID;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { MetadataDescriptor } from '@topconsultnpm/sdk-ts';
|
|
2
|
+
import { IColumnProps } from 'devextreme-react/data-grid';
|
|
3
|
+
export declare const needsDisplayValue: (columnMd: MetadataDescriptor | undefined) => boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Resa dei valori nelle griglie di TMDistinctValues: al posto della chiave archiviata mostra la descrizione
|
|
6
|
+
* presa dalle cache di liste dati e utenti, tenendole allineate alle colonne effettivamente visualizzate.
|
|
7
|
+
*
|
|
8
|
+
* Vale per entrambe le sorgenti del pannello (valori distinti e ricerca rapida, che può essere su un altro
|
|
9
|
+
* tipo documento): le chiavi delle due sorgenti vengono accumulate e ricaricate sempre insieme, altrimenti
|
|
10
|
+
* un caricamento farebbe perdere le descrizioni dell'altra sorgente.
|
|
11
|
+
*/
|
|
12
|
+
export declare const useTMDistinctValuesMetadataDisplay: () => {
|
|
13
|
+
loadDisplayCachesAsync: (columnsMd: Array<MetadataDescriptor | undefined>, rows: string[][] | undefined) => Promise<void>;
|
|
14
|
+
getMetadataDisplayValue: (columnMd: MetadataDescriptor | undefined, value: any) => any;
|
|
15
|
+
displayValueColumnProps: (columnMd: MetadataDescriptor | undefined, dataField: string) => Partial<IColumnProps>;
|
|
16
|
+
};
|
|
17
|
+
/** Tipo di ritorno dell'hook: le sorgenti di TMDistinctValues lo ricevono come dipendenza */
|
|
18
|
+
export type TMDistinctValuesMetadataDisplay = ReturnType<typeof useTMDistinctValuesMetadataDisplay>;
|