@topconsultnpm/sdkui-react-beta 6.17.40 → 6.17.42
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) => {
|
|
@@ -304,7 +304,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
|
|
|
304
304
|
gap: '10px',
|
|
305
305
|
flex: '1 1 auto',
|
|
306
306
|
minWidth: 0,
|
|
307
|
-
}, children: [_jsx(TMButton, { btnStyle: 'advanced', icon: _jsx(IconSearch, {}), showTooltip: false, width: '
|
|
307
|
+
}, children: [_jsx(TMButton, { btnStyle: 'advanced', icon: _jsx(IconSearch, {}), showTooltip: false, width: '90px', caption: SDKUI_Localizator.Search, advancedColor: '#4A96D2', onClick: handleSearchButtonClick }), _jsx(TMButton, { width: '90px', btnStyle: 'advanced', advancedType: 'primary', showTooltip: false, caption: SDKUI_Localizator.Clear, icon: _jsx(IconClear, {}), advancedColor: 'white', color: 'primaryOutline', onClick: clearFilters })] }), (!showAdvancedSearch && qd?.where && qd.where.length > initialMaxItems) && (_jsx("div", { style: { flex: '0 0 auto' }, children: _jsx(TMButton, { width: '120px', btnStyle: isMobile ? 'icon' : 'advanced', advancedColor: TMColors.button_primary, caption: captionText, showTooltip: false, icon: isMobile ? (_jsx("div", { style: {
|
|
308
308
|
backgroundColor: TMColors.button_primary,
|
|
309
309
|
minWidth: '30px',
|
|
310
310
|
minHeight: '30px',
|