@topconsultnpm/sdkui-react-beta 6.17.39 → 6.17.41
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.
|
@@ -53,7 +53,6 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
53
53
|
const [formData, setFormData] = useState([]);
|
|
54
54
|
const [formDataOrig, setFormDataOrig] = useState([]);
|
|
55
55
|
const [validationItems, setValidationItems] = useState([]);
|
|
56
|
-
const [changedMetadata, setChangedMetadata] = useState([]);
|
|
57
56
|
const [fromDTD, setFromDTD] = useState();
|
|
58
57
|
const [showApprovePopup, setShowApprovePopup] = useState(false);
|
|
59
58
|
const [showRejectPopup, setShowRejectPopup] = useState(false);
|
|
@@ -61,6 +60,13 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
61
60
|
const [showMoreInfoPopup, setShowMoreInfoPopup] = useState(false);
|
|
62
61
|
const [layout, setLayout] = useState();
|
|
63
62
|
const appliedInputMidsRef = useRef(null);
|
|
63
|
+
// Refs per evitare stale closure nei callback
|
|
64
|
+
// I useCallback catturano i valori delle dipendenze al momento della creazione.
|
|
65
|
+
// Questi ref vengono sincronizzati tramite useEffect e permettono di accedere
|
|
66
|
+
// sempre ai valori correnti senza dover ricreare i callback ad ogni cambio di stato.
|
|
67
|
+
const formDataOrigRef = useRef([]);
|
|
68
|
+
const formDataRef = useRef([]);
|
|
69
|
+
const fromDTDRef = useRef();
|
|
64
70
|
const [isOpenDetails, setIsOpenDetails] = useState(false);
|
|
65
71
|
const [isOpenMaster, setIsOpenMaster] = useState(false);
|
|
66
72
|
const [secondaryMasterDcmts, setSecondaryMasterDcmts] = useState([]);
|
|
@@ -129,6 +135,8 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
129
135
|
}
|
|
130
136
|
setFormDataOrig(structuredClone(metadataList));
|
|
131
137
|
setFormData(structuredClone(metadataList));
|
|
138
|
+
// Sincronizza il ref con i dati caricati per evitare stale closure in handleSave
|
|
139
|
+
formDataOrigRef.current = structuredClone(metadataList);
|
|
132
140
|
}
|
|
133
141
|
catch (e) {
|
|
134
142
|
TMExceptionBoxManager.show({ exception: e });
|
|
@@ -157,6 +165,7 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
157
165
|
const metadataList = searchResultToMetadataValues(dtd?.id, undefined, [], [], renderedMetadata, layoutMode);
|
|
158
166
|
setFormDataOrig(structuredClone(metadataList));
|
|
159
167
|
setFormData(structuredClone(metadataList));
|
|
168
|
+
formDataOrigRef.current = structuredClone(metadataList);
|
|
160
169
|
}
|
|
161
170
|
let resLayout = await SDK_Globals.tmSession?.NewDcmtTypeEngine().LayoutRetrieveAsync(TID, layoutMode);
|
|
162
171
|
setLayout(resLayout);
|
|
@@ -192,6 +201,16 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
192
201
|
return changes;
|
|
193
202
|
}, [createChange]);
|
|
194
203
|
useEffect(() => { setID(genUniqueId()); }, []);
|
|
204
|
+
// Sincronizza i ref con gli stati correnti per evitare stale closure nei callback.
|
|
205
|
+
// I useCallback catturano le variabili delle dipendenze al momento della loro creazione,
|
|
206
|
+
// quindi se uno stato cambia frequentemente, il callback avrebbe sempre valori vecchi.
|
|
207
|
+
// Usando i ref, possiamo accedere sempre ai valori più recenti senza dover ricreare i callback.
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
formDataRef.current = formData;
|
|
210
|
+
}, [formData]);
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
fromDTDRef.current = fromDTD;
|
|
213
|
+
}, [fromDTD]);
|
|
195
214
|
useEffect(() => {
|
|
196
215
|
if (!inputFile || inputFile === null)
|
|
197
216
|
return;
|
|
@@ -227,8 +246,6 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
227
246
|
useEffect(() => {
|
|
228
247
|
if (formData.length > 0) {
|
|
229
248
|
setValidationItems(validateMetadataList(formData));
|
|
230
|
-
let changes = getSpecificChangedKeysWithValues(formDataOrig, formData);
|
|
231
|
-
setChangedMetadata(changes);
|
|
232
249
|
const newDcmt = {
|
|
233
250
|
tid: formData.find(o => o.mid == SystemMIDsAsNumber.TID)?.value,
|
|
234
251
|
did: formData.find(o => o.mid == SystemMIDsAsNumber.DID)?.value,
|
|
@@ -426,7 +443,12 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
426
443
|
ue.Metadata_ClearAll();
|
|
427
444
|
try {
|
|
428
445
|
TMSpinner.show({ description: 'Aggiornamento in corso...' });
|
|
429
|
-
|
|
446
|
+
// Usa i ref invece degli stati per evitare stale closure.
|
|
447
|
+
// handleSave è un useCallback che verrebbe ricreato ogni volta che formData/formDataOrig cambiano,
|
|
448
|
+
// causando problemi di performance. Usando i ref, possiamo mantenere lo stesso callback
|
|
449
|
+
// ma accedere sempre ai valori correnti tramite formDataRef.current e formDataOrigRef.current.
|
|
450
|
+
const changes = getSpecificChangedKeysWithValues(formDataOrigRef.current, formDataRef.current);
|
|
451
|
+
for (let metadata of changes) {
|
|
430
452
|
if (!metadata.modifiedValue)
|
|
431
453
|
ue.Metadata_AddNull(metadata.mid);
|
|
432
454
|
else {
|
|
@@ -447,7 +469,9 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
447
469
|
await ue.UpdateAsync();
|
|
448
470
|
// Aggiorna lo stato locale immediatamente dopo il salvataggio riuscito
|
|
449
471
|
// Questo garantisce che isModified diventi false anche se il reload fallisce
|
|
450
|
-
|
|
472
|
+
const savedFormData = structuredClone(formDataRef.current);
|
|
473
|
+
setFormDataOrig(savedFormData);
|
|
474
|
+
formDataOrigRef.current = savedFormData;
|
|
451
475
|
// Tenta di ottenere i metadati aggiornati
|
|
452
476
|
let metadataResult = undefined;
|
|
453
477
|
let hasGetMetadataError = false;
|
|
@@ -487,8 +511,12 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
487
511
|
await onSavedAsyncCallback?.(TID, DID, metadataResult === null ? null : metadataResult);
|
|
488
512
|
// Mostra messaggio di successo solo se non ci sono stati errori critici
|
|
489
513
|
if (!hasGetMetadataError) {
|
|
490
|
-
if (metadataResult && metadataResult !== null)
|
|
491
|
-
|
|
514
|
+
if (metadataResult && metadataResult !== null) {
|
|
515
|
+
// Usa fromDTDRef.current invece di fromDTD per evitare stale closure.
|
|
516
|
+
// fromDTD potrebbe essere undefined/vuoto nel callback se lo stato è cambiato,
|
|
517
|
+
// mentre fromDTDRef.current contiene sempre il valore corrente sincronizzato tramite useEffect.
|
|
518
|
+
await setMetadataList(fromDTDRef.current?.metadata ?? [], metadataResult);
|
|
519
|
+
}
|
|
492
520
|
ShowAlert({ mode: 'success', title: 'Form di documento', message: 'Le modifiche sono state salvate con successo', duration: 3000 });
|
|
493
521
|
}
|
|
494
522
|
else
|
|
@@ -500,7 +528,7 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
500
528
|
finally {
|
|
501
529
|
TMSpinner.hide();
|
|
502
530
|
}
|
|
503
|
-
}, [DID, TID,
|
|
531
|
+
}, [DID, TID, getSpecificChangedKeysWithValues, onSavedAsyncCallback, onClose, setMetadataList]);
|
|
504
532
|
const handleArchiveCompleted = useCallback(async () => {
|
|
505
533
|
let firstBlock = true;
|
|
506
534
|
let maxFileSize = 0;
|
|
@@ -630,7 +658,7 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
630
658
|
if (e == ButtonNames.CANCEL)
|
|
631
659
|
return;
|
|
632
660
|
if (e == ButtonNames.YES)
|
|
633
|
-
await handleSave
|
|
661
|
+
await handleSave();
|
|
634
662
|
onClose?.();
|
|
635
663
|
}
|
|
636
664
|
catch (ex) {
|
|
@@ -668,27 +696,55 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
|
|
|
668
696
|
setShowAll(true);
|
|
669
697
|
}
|
|
670
698
|
}, [shouldShowAll, showAll]);
|
|
671
|
-
const tmDcmtForm = useMemo(() =>
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
699
|
+
const tmDcmtForm = useMemo(() => {
|
|
700
|
+
return _jsx(_Fragment, { children: metadataValuesSource.length > 0 &&
|
|
701
|
+
_jsxs(StyledToolbarCardContainer, { children: [_jsx(TMMetadataValues, { TID: TID, metadataValues: metadataValuesSource, metadataValuesOrig: metadataValuesSourceOrig, isExpertMode: isExpertMode, isOpenDistinctValues: isOpenDistinctValues, openChooserBySingleClick: !isOpenDistinctValues, selectedMID: focusedMetadataValue?.mid, isReadOnly: formMode === FormModes.ReadOnly, layoutMode: layoutMode, deviceType: deviceType, validationItems: validationItems, inputMids: inputMids, layout: layout, onFocusedItemChanged: (item) => { (item?.mid !== focusedMetadataValue?.mid) && setFocusedMetadataValue(item); }, onValueChanged: (newItems) => {
|
|
702
|
+
setFormData((prevItems) => prevItems.map((item) => {
|
|
703
|
+
const newItem = newItems.find((newItem) => newItem.tid === item.tid && newItem.mid === item.mid);
|
|
704
|
+
return newItem ? { ...item, ...newItem } : item;
|
|
705
|
+
}));
|
|
706
|
+
}, onAdvancedMenuClick: (e) => {
|
|
707
|
+
switch (e.button) {
|
|
708
|
+
case AdvancedMenuButtons.DistinctValues:
|
|
709
|
+
setIsOpenDistinctValues(!isOpenDistinctValues);
|
|
710
|
+
break;
|
|
711
|
+
case AdvancedMenuButtons.FormulaEditor:
|
|
712
|
+
setIsOpenFormulaEditor(!isOpenFormulaEditor);
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
} }), _jsxs(StyledFormButtonsContainer, { children: [_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 10 }, children: _jsx("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '8px' }, children: layoutMode === LayoutModes.Update ? _jsxs(_Fragment, { children: [_jsx(TMSaveFormButtonSave, { showTooltip: false, btnStyle: 'advanced', advancedColor: '#f09c0a', isModified: isModified, formMode: formMode, errorsCount: validationItems.filter(o => o.ResultType == ResultTypes.ERROR).length, onSaveAsync: handleConfirmAction }), _jsx(TMSaveFormButtonUndo, { btnStyle: 'toolbar', showTooltip: true, color: 'primary', isModified: isModified, formMode: formMode, onUndo: handleUndo })] }) :
|
|
716
|
+
_jsxs(_Fragment, { children: [_jsx(TMButton, { disabled: archiveBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconBoxArchiveIn, {}), width: 'auto', showTooltip: false, caption: SDKUI_Localizator.Archive, advancedColor: TMColors.success, onClick: handleConfirmAction }), _jsx(TMButton, { disabled: !clearFormBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconClear, {}), width: 'auto', showTooltip: false, caption: SDKUI_Localizator.Clear, advancedColor: TMColors.tertiary, onClick: handleClearForm }), DID && _jsx(TMButton, { disabled: undoBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconUndo, {}), width: '150px', showTooltip: false, caption: SDKUI_Localizator.Undo, advancedColor: TMColors.tertiary, onClick: handleUndo })] }) }) }), totalItems > listMaxItems &&
|
|
717
|
+
!isApprView &&
|
|
718
|
+
TID !== SystemTIDs.Drafts &&
|
|
719
|
+
!shouldShowAll &&
|
|
720
|
+
_jsx(TMShowAllOrMaxItemsButton, { showAll: showAll, dataSourceLength: totalItems, onClick: () => { setShowAll(!showAll); } })] }), _jsx(ConfirmAttachmentsDialog, {})] }) });
|
|
721
|
+
}, [
|
|
722
|
+
TID,
|
|
723
|
+
DID,
|
|
724
|
+
metadataValuesSource,
|
|
725
|
+
metadataValuesSourceOrig,
|
|
726
|
+
isExpertMode,
|
|
727
|
+
isOpenDistinctValues,
|
|
728
|
+
focusedMetadataValue?.mid,
|
|
729
|
+
formMode,
|
|
730
|
+
layoutMode,
|
|
731
|
+
deviceType,
|
|
732
|
+
validationItems,
|
|
733
|
+
inputMids,
|
|
734
|
+
layout,
|
|
735
|
+
isModified,
|
|
736
|
+
archiveBtnDisabled,
|
|
737
|
+
clearFormBtnDisabled,
|
|
738
|
+
undoBtnDisabled,
|
|
739
|
+
totalItems,
|
|
740
|
+
listMaxItems,
|
|
741
|
+
isApprView,
|
|
742
|
+
shouldShowAll,
|
|
743
|
+
showAll,
|
|
744
|
+
handleConfirmAction,
|
|
745
|
+
handleUndo,
|
|
746
|
+
handleClearForm
|
|
747
|
+
]);
|
|
692
748
|
const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: TID, did: DID }), [TID, DID]);
|
|
693
749
|
const tmSysMetadata = useMemo(() => _jsx(TMMetadataValues, { layoutMode: layoutMode, openChooserBySingleClick: !isOpenDistinctValues, TID: TID, isReadOnly: true, deviceType: deviceType, metadataValues: formData.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: formData.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [], inputMids: inputMids }), [TID, layoutMode, formData, deviceType, inputMids]);
|
|
694
750
|
const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { currentDcmt: currentDcmt, dcmtFile: dcmtFile ?? inputFile, deviceType: deviceType, fromDTD: fromDTD, layoutMode: layoutMode, onFileUpload: (setFile) => {
|
|
@@ -78,6 +78,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
|
78
78
|
const [isOpenArchiveRelationForm, setIsOpenArchiveRelationForm] = useState(false);
|
|
79
79
|
const [archiveRelatedDcmtFormTID, setArchiveRelatedDcmtFormTID] = useState(undefined);
|
|
80
80
|
const [archiveRelatedDcmtFormMids, setArchiveRelatedDcmtFormMids] = useState([]);
|
|
81
|
+
const [relatedDcmtsChooserDataSource, setRelatedDcmtsChooserDataSource] = useState(undefined);
|
|
81
82
|
const [secondaryMasterDcmts, setSecondaryMasterDcmts] = useState([]);
|
|
82
83
|
const [isOpenDcmtForm, setIsOpenDcmtForm] = useState(false);
|
|
83
84
|
const [currentTIDHasDetailRelations, setCurrentTIDHasDetailRelations] = useState();
|
|
@@ -453,6 +454,9 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
|
453
454
|
const filterRelationsWithAssociations = (relations) => {
|
|
454
455
|
return relations.filter(rel => rel.associations && rel.associations.length > 0);
|
|
455
456
|
};
|
|
457
|
+
const getRelatedDcmt = async (relation, type) => {
|
|
458
|
+
return await DcmtTypeListCacheService.GetAsync(type === 'detail' ? relation.detailTID : relation.masterTID);
|
|
459
|
+
};
|
|
456
460
|
const showNoRelationsAlert = (type) => {
|
|
457
461
|
ShowAlert({
|
|
458
462
|
message: type === 'detail'
|
|
@@ -507,6 +511,12 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
|
507
511
|
}
|
|
508
512
|
setRelatedDcmts(withAssociations);
|
|
509
513
|
if (withAssociations.length > 1) {
|
|
514
|
+
const dataSourcePromises = withAssociations.map(async (rel) => {
|
|
515
|
+
const relatedDcmt = await getRelatedDcmt(rel, type);
|
|
516
|
+
return { id: rel?.id, name: relatedDcmt?.name };
|
|
517
|
+
});
|
|
518
|
+
const dataSource = await Promise.all(dataSourcePromises);
|
|
519
|
+
setRelatedDcmtsChooserDataSource(dataSource);
|
|
510
520
|
setArchiveType(type);
|
|
511
521
|
setShowRelatedDcmtsChooser(true);
|
|
512
522
|
}
|
|
@@ -554,7 +564,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
|
554
564
|
await refreshSelectionDataRowsAsync();
|
|
555
565
|
}, onStatusChanged: (isModified) => { setIsModifiedBatchUpdate(isModified); } }), (showToppyForApprove && !showApprovePopup && !showRejectPopup && !showReAssignPopup && !showMoreInfoPopup && !openS4TViewer && !showTodoDcmtForm) &&
|
|
556
566
|
_jsx(ToppyHelpCenter, { deviceType: deviceType, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => setShowApprovePopup(true), onSignApprove: handleSignApprove, onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), onMoreInfo: () => setShowMoreInfoPopup(true), approveDisable: disable, signApproveDisable: disableSignApproveDisable, rejectDisable: disable, reassignDisable: disable, infoDisable: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length !== 1 }) }) })] }), _jsx(ConfirmFormatDialog, {}), _jsx(ConfirmAttachmentsDialog, {}), showRelatedDcmtsChooser &&
|
|
557
|
-
_jsx(TMChooserForm, { dataSource:
|
|
567
|
+
_jsx(TMChooserForm, { dataSource: relatedDcmtsChooserDataSource, onChoose: async (selectedRelation) => {
|
|
558
568
|
try {
|
|
559
569
|
setShowRelatedDcmtsChooser(false);
|
|
560
570
|
TMSpinner.show({ description: SDKUI_Localizator.Loading });
|
|
@@ -594,7 +604,8 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
|
|
|
594
604
|
openS4TViewer,
|
|
595
605
|
showRelatedDcmtsChooser,
|
|
596
606
|
relatedDcmts,
|
|
597
|
-
setShowRelatedDcmtsChooser
|
|
607
|
+
setShowRelatedDcmtsChooser,
|
|
608
|
+
relatedDcmtsChooserDataSource
|
|
598
609
|
]);
|
|
599
610
|
const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: focusedItem?.TID, did: focusedItem?.DID }), [focusedItem]);
|
|
600
611
|
const tmSysMetadata = useMemo(() => _jsx(TMMetadataValues, { layoutMode: LayoutModes.Update, openChooserBySingleClick: true, TID: focusedItem?.TID, isReadOnly: true, deviceType: deviceType, metadataValues: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [] }), [focusedItem, currentMetadataValues, deviceType]);
|