@topconsultnpm/sdkui-react 6.22.0-dev2.39 → 6.22.0-dev2.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +5 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +20 -4
- package/lib/helper/SDKUI_Localizator.d.ts +1 -0
- package/lib/helper/SDKUI_Localizator.js +10 -0
- package/lib/hooks/useDocumentOperations.js +5 -1
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +9 -1
- package/lib/hooks/useMultiMasterDetailDcmts.js +57 -11
- package/package.json +1 -1
|
@@ -8,6 +8,11 @@ interface TMMultiMasterDetailDcmtsFormProps {
|
|
|
8
8
|
masterDcmt: DcmtInfo;
|
|
9
9
|
/** Funzione per la chiusura del modale */
|
|
10
10
|
onClose: () => void;
|
|
11
|
+
/** true se nella lista da cui è stato aperto il modale c'è un documento precedente/successivo */
|
|
12
|
+
canNavigateMaster?: (dir: 'next' | 'prev') => boolean;
|
|
13
|
+
/** Sposta il focus della lista di provenienza: il master del modale segue quel documento.
|
|
14
|
+
* Senza questa callback le frecce di navigazione non si mostrano */
|
|
15
|
+
onNavigateMaster?: (dir: 'next' | 'prev') => void;
|
|
11
16
|
allTasks?: Array<TaskDescriptor>;
|
|
12
17
|
getAllTasks?: () => Promise<void>;
|
|
13
18
|
deleteTaskByIdsCallback?: (deletedTaskIds: Array<number>) => Promise<void>;
|
|
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { LayoutModes } from "@topconsultnpm/sdk-ts";
|
|
4
4
|
import { LoadIndicator } from "devextreme-react/load-indicator";
|
|
5
5
|
import { SearchResultContext } from "../../../ts";
|
|
6
|
-
import { getFullFileExtension, getMultiDetailModalSize, getMultiDetailPanelStates, hasMultiDetailPanelStates, hoverStyle, IconArchive, IconDelete, IconDuplicate, IconEdit, IconEraser, IconInfo, IconMenuVertical, IconRefresh, IconSearch, IconSearchCheck, saveMultiDetailModalSize, saveMultiDetailPanelStates, SDKUI_Localizator } from "../../../helper";
|
|
6
|
+
import { getFullFileExtension, getMultiDetailModalSize, getMultiDetailPanelStates, hasMultiDetailPanelStates, hoverStyle, IconArchive, IconArrowDown, IconArrowUp, IconDelete, IconDuplicate, IconEdit, IconEraser, IconInfo, IconMenuVertical, IconRefresh, IconSearch, IconSearchCheck, saveMultiDetailModalSize, saveMultiDetailPanelStates, SDKUI_Localizator } from "../../../helper";
|
|
7
7
|
import { TMColors } from "../../../utils/theme";
|
|
8
8
|
import { DeviceType, useDeviceType } from "../../base/TMDeviceProvider";
|
|
9
9
|
import { TMLayoutWaitingContainer } from "../../base/TMWaitPanel";
|
|
@@ -26,6 +26,8 @@ const styles = {
|
|
|
26
26
|
header: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '6px 12px', borderBottom: `1px solid ${TMColors.card_background}`, flexShrink: 0 },
|
|
27
27
|
// Opzioni nella barra del titolo: stesse misure e colore della chiusura DevExtreme (.dx-popup-title .dx-closebutton)
|
|
28
28
|
titleMenuIcon: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '18px', height: '18px', margin: '1px 0 0', color: `var(--dx-color-text, ${TMColors.text_normal})`, cursor: 'pointer' },
|
|
29
|
+
titleMenuIconDisabled: { cursor: 'default', opacity: 0.4 },
|
|
30
|
+
titleActions: { display: 'inline-flex', alignItems: 'center', gap: '10px' },
|
|
29
31
|
loader: { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center' },
|
|
30
32
|
summaryContainer: { display: 'flex', flexDirection: 'column', flex: 1, padding: '8px 12px 10px', minHeight: 0, gap: '8px' },
|
|
31
33
|
// I pannelli ripartiscono l'altezza in percentuale: serve un'altezza definita che non dipenda dal
|
|
@@ -45,8 +47,8 @@ const styles = {
|
|
|
45
47
|
/** Sezione mostrata per prima su mobile, dove i pannelli si vedono uno alla volta */
|
|
46
48
|
const MOBILE_SECTION = 'archiveList';
|
|
47
49
|
const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
48
|
-
const { dtd, masterDcmt, onClose, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showDcmtFormSidebar, showTodoDcmtForm, openFileUploaderPdfEditor, graphometricManager, editPdfForm, openS4TViewer, showToppyDraggableHelpCenter, toppyHelpCenterUsePortal, fetchRemoteCertificates, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onReferenceClick, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, onFileOpened, passToArchiveCallback, openWGsCopyMoveForm } = props;
|
|
49
|
-
const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, hasSavedLayout, setHasSavedLayout, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, copyArchivedDetailToRows, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
|
|
50
|
+
const { dtd, masterDcmt, onClose, canNavigateMaster, onNavigateMaster, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showDcmtFormSidebar, showTodoDcmtForm, openFileUploaderPdfEditor, graphometricManager, editPdfForm, openS4TViewer, showToppyDraggableHelpCenter, toppyHelpCenterUsePortal, fetchRemoteCertificates, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onReferenceClick, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, onFileOpened, passToArchiveCallback, openWGsCopyMoveForm } = props;
|
|
51
|
+
const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, isMasterNavigationEnabled, canNavigateMasterPrev, canNavigateMasterNext, navigateMaster, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, hasSavedLayout, setHasSavedLayout, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, copyArchivedDetailToRows, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose, canNavigateMaster, onNavigateMaster });
|
|
50
52
|
const deviceType = useDeviceType();
|
|
51
53
|
// Su mobile il panel manager mostra un pannello alla volta: le sezioni si raggiungono dalla sua barra
|
|
52
54
|
const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
|
|
@@ -176,6 +178,20 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
176
178
|
return undefined;
|
|
177
179
|
return (_jsx(ContextMenu, { trigger: 'left', items: optionsMenuItems, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Options, children: _jsx("span", { style: styles.titleMenuIcon, "aria-label": SDKUI_Localizator.Options, children: _jsx(IconMenuVertical, { fontSize: 16, color: "white" }) }) }) }));
|
|
178
180
|
};
|
|
181
|
+
const renderMasterNavigation = () => {
|
|
182
|
+
if (!isMasterNavigationEnabled)
|
|
183
|
+
return undefined;
|
|
184
|
+
const renderArrow = (dir, isEnabled, icon, tooltip) => (_jsx(TMTooltip, { content: tooltip, children: _jsx("span", { role: 'button', "aria-label": tooltip, "aria-disabled": !isEnabled, onClick: isEnabled ? () => navigateMaster(dir) : undefined, style: { ...styles.titleMenuIcon, ...(isEnabled ? {} : styles.titleMenuIconDisabled) }, children: icon }) }));
|
|
185
|
+
return (_jsxs(_Fragment, { children: [renderArrow('prev', canNavigateMasterPrev, _jsx(IconArrowUp, { fontSize: 16, color: "white" }), SDKUI_Localizator.Previous), renderArrow('next', canNavigateMasterNext, _jsx(IconArrowDown, { fontSize: 16, color: "white" }), SDKUI_Localizator.Next)] }));
|
|
186
|
+
};
|
|
187
|
+
// Azioni nella barra del titolo: navigazione del master e opzioni del layout
|
|
188
|
+
const renderTitleActions = () => {
|
|
189
|
+
const navigation = renderMasterNavigation();
|
|
190
|
+
const optionsMenu = renderOptionsMenu();
|
|
191
|
+
if (!navigation && !optionsMenu)
|
|
192
|
+
return undefined;
|
|
193
|
+
return _jsxs("div", { style: styles.titleActions, children: [navigation, optionsMenu] });
|
|
194
|
+
};
|
|
179
195
|
// ----- Sezione: informazioni del master -----------------------------------------------------
|
|
180
196
|
const renderMasterInfo = (panel) => {
|
|
181
197
|
const isOpen = sectionsOpen.masterInfo;
|
|
@@ -287,7 +303,7 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
287
303
|
return null;
|
|
288
304
|
return _jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { direction: 'vertical', ...waitPanel, isCancelable: true, abortController: abortController, usePortal: true, children: _jsx(TMModal, { width: modalSize.width, height: modalSize.height, onClose: closeModal, onResized: persistModalSize,
|
|
289
305
|
// Cancellato il layout il popup torna alle dimensioni di default
|
|
290
|
-
sizeResetToken: layoutResetToken, title: modalTitle, hidePopup: false, askClosingConfirm: archiveRows.length > 0, recenterOnResize: true, titleActions:
|
|
306
|
+
sizeResetToken: layoutResetToken, title: modalTitle, hidePopup: false, askClosingConfirm: archiveRows.length > 0, recenterOnResize: true, titleActions: renderTitleActions(), children: _jsxs("div", { style: styles.content, children: [hasMultipleTypes && _jsx("div", { style: styles.header, children: renderStepper({
|
|
291
307
|
steps,
|
|
292
308
|
phase,
|
|
293
309
|
hasMultipleTypes,
|
|
@@ -96,6 +96,7 @@ export declare class SDKUI_Localizator {
|
|
|
96
96
|
static get CancelCheckOut(): string;
|
|
97
97
|
static get Cancel(): "Abbrechen" | "Cancel" | "Anular" | "Annuler" | "Cancelar" | "Annulla";
|
|
98
98
|
static get CancelAndClose(): string;
|
|
99
|
+
static get ChangeMasterWithoutArchiving_Confirm(): "Beim Wechsel des Dokuments werden die eingegebenen Details verworfen und nicht archiviert. Fortfahren?" | "Changing document will discard the entered details without archiving them. Continue?" | "Al cambiar de documento, los detalles introducidos se descartarán sin archivarse. ¿Continuar?" | "En changeant de document, les détails saisis seront abandonnés sans être archivés. Continuer ?" | "Ao mudar de documento, os detalhes introduzidos serão descartados sem serem arquivados. Continuar?" | "Cambiando documento, i dettagli inseriti verranno scartati senza essere archiviati. Continuare?";
|
|
99
100
|
static get ChangePassword(): "Kennwort ändern" | "Change password" | "Cambiar la contraseña" | "Changer le mot de passe" | "Alterar a senha" | "Cambia password";
|
|
100
101
|
static get ChangeStatusTo(): string;
|
|
101
102
|
static get CharactersRemaining(): "verbleibende Zeichen" | "characters remaining" | "caracteres restantes" | "caractères restants" | "caratteri rimanenti";
|
|
@@ -915,6 +915,16 @@ export class SDKUI_Localizator {
|
|
|
915
915
|
default: return "Annulla e chiudi";
|
|
916
916
|
}
|
|
917
917
|
}
|
|
918
|
+
static get ChangeMasterWithoutArchiving_Confirm() {
|
|
919
|
+
switch (this._cultureID) {
|
|
920
|
+
case CultureIDs.De_DE: return "Beim Wechsel des Dokuments werden die eingegebenen Details verworfen und nicht archiviert. Fortfahren?";
|
|
921
|
+
case CultureIDs.En_US: return "Changing document will discard the entered details without archiving them. Continue?";
|
|
922
|
+
case CultureIDs.Es_ES: return "Al cambiar de documento, los detalles introducidos se descartarán sin archivarse. ¿Continuar?";
|
|
923
|
+
case CultureIDs.Fr_FR: return "En changeant de document, les détails saisis seront abandonnés sans être archivés. Continuer ?";
|
|
924
|
+
case CultureIDs.Pt_PT: return "Ao mudar de documento, os detalhes introduzidos serão descartados sem serem arquivados. Continuar?";
|
|
925
|
+
default: return "Cambiando documento, i dettagli inseriti verranno scartati senza essere archiviati. Continuare?";
|
|
926
|
+
}
|
|
927
|
+
}
|
|
918
928
|
static get ChangePassword() {
|
|
919
929
|
switch (this._cultureID) {
|
|
920
930
|
case CultureIDs.De_DE: return "Kennwort ändern";
|
|
@@ -1371,6 +1371,10 @@ export const useDocumentOperations = (props) => {
|
|
|
1371
1371
|
dcmtUtility,
|
|
1372
1372
|
fetchRemoteCertificates
|
|
1373
1373
|
};
|
|
1374
|
+
// Navigazione del master nell'archiviazione multipla dei dettagli: il modale prende il documento
|
|
1375
|
+
// con il focus, quindi le frecce hanno senso solo senza selezione a checkbox, che ha la precedenza
|
|
1376
|
+
// sul focus e terrebbe il master fermo (vedi getSelectedDcmtsOrFocused)
|
|
1377
|
+
const canNavigateMultiDetailMaster = !!canNavigateHandler && !!onNavigateHandler && selectedItems.length === 0 && selectedDcmtInfos.length === 1;
|
|
1374
1378
|
const masterDetailMasterCommonProps = {
|
|
1375
1379
|
deviceType,
|
|
1376
1380
|
isForMaster: true,
|
|
@@ -1408,7 +1412,7 @@ export const useDocumentOperations = (props) => {
|
|
|
1408
1412
|
onRefreshPreviewDatagrid
|
|
1409
1413
|
}, fetchRemoteCertificates: fetchRemoteCertificates, graphometricManager: graphometricManager })) }), (showHistory && dtd && selectedDcmtInfos.length > 0) && _jsx(TMViewHistoryDcmt, { fromDTD: dtd, deviceType: deviceType, inputDcmt: selectedDcmtInfos[0], onClose: hideHistoryCallback, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), (commentFormState.show && selectedDcmtInfos.length > 0) && _jsx(TMBlogCommentForm, { context: { engine: 'SearchEngine', object: { tid: selectedDcmtInfos[0].TID, did: selectedDcmtInfos[0].DID } }, onClose: hideCommentFormCallback, refreshCallback: onRefreshBlog, participants: [], showAttachmentsSection: true, allArchivedDocumentsFileItems: convertSearchResultDescriptorToFileItems(currentSearchResults ?? []), isCommentRequired: commentFormState.isRequired, removeAndEditAttachment: commentFormState.removeAndEditAttachment, selectedAttachmentDid: [Number(selectedDcmtInfos[0].DID)] }), (showCheckoutInformationForm && dtd && selectedDcmtInfos.length > 0) &&
|
|
1410
1414
|
_jsx(TMDcmtCheckoutInfoForm, { dtdName: dtd.name ?? SDKUI_Localizator.SearchResult, selectedDcmtOrFocused: selectedDcmtInfos[0], onClose: hideCheckoutInformationFormCallback }), isOpenDetailsModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenDetailsModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailDetailsCommonProps, onBack: () => setIsOpenDetailsModal(false) }) }), _jsx(StyledMultiViewPanel, { "$isVisible": isOpenDetails, children: isOpenDetails && _jsx(TMMasterDetailDcmts, { ...masterDetailDetailsCommonProps, onBack: () => setIsOpenDetails(false) }) }), (isOpenMultiDetailModal && dtd) &&
|
|
1411
|
-
_jsx(TMMultiMasterDetailDcmtsForm, { dtd: dtd, masterDcmt: selectedDcmtInfos[0], onClose: () => setIsOpenMultiDetailModal(false), allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showDcmtFormSidebar: showDcmtFormSidebar, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm }), isOpenMasterModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenMasterModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMasterModal(false) }) }), _jsxs(StyledMultiViewPanel, { "$isVisible": isOpenMaster, children: [isOpenMaster && _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMaster(false) }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
|
|
1415
|
+
_jsx(TMMultiMasterDetailDcmtsForm, { dtd: dtd, masterDcmt: selectedDcmtInfos[0], onClose: () => setIsOpenMultiDetailModal(false), canNavigateMaster: canNavigateMultiDetailMaster ? canNavigateHandler : undefined, onNavigateMaster: canNavigateMultiDetailMaster ? onNavigateHandler : undefined, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showDcmtFormSidebar: showDcmtFormSidebar, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm }), isOpenMasterModal && _jsx(TMModal, { width: "95%", height: "95%", onClose: () => setIsOpenMasterModal(false), title: SDKUI_Localizator.Relations, children: _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMasterModal(false) }) }), _jsxs(StyledMultiViewPanel, { "$isVisible": isOpenMaster, children: [isOpenMaster && _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, onBack: () => setIsOpenMaster(false) }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
|
|
1412
1416
|
return (_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { ...masterDetailMasterCommonProps, inputDcmts: [dcmt], allowNavigation: false, onBack: () => handleRemoveItem(dcmt.TID, dcmt.DID) }) }, `${index}-${dcmt.DID}`));
|
|
1413
1417
|
})] }), isOpenArchiveRelationForm && _jsx(TMDcmtForm, { isModal: true, titleModal: SDKUI_Localizator.Archive + ' - ' + (archiveType === 'detail' ? SDKUI_Localizator.DcmtsDetail : SDKUI_Localizator.DcmtsMaster), TID: archiveRelatedDcmtFormTID, layoutMode: LayoutModes.Ark, inputMids: archiveRelatedDcmtFormMids, showBackButton: false, allowButtonsRefs: false, onClose: () => {
|
|
1414
1418
|
setIsOpenArchiveRelationForm(false);
|
|
@@ -36,12 +36,16 @@ interface UseMultiMasterDetailDcmtsParams {
|
|
|
36
36
|
masterDcmt: DcmtInfo;
|
|
37
37
|
/** Chiusura del modale */
|
|
38
38
|
onClose: () => void;
|
|
39
|
+
/** true se nella lista di provenienza c'è un documento precedente/successivo su cui spostare il master */
|
|
40
|
+
canNavigateMaster?: (dir: 'next' | 'prev') => boolean;
|
|
41
|
+
/** Sposta il focus della lista di provenienza: il master arriva da lì e cambia da sé (vedi masterDcmt) */
|
|
42
|
+
onNavigateMaster?: (dir: 'next' | 'prev') => void;
|
|
39
43
|
}
|
|
40
44
|
/**
|
|
41
45
|
* Logica dell'archiviazione multipla dei dettagli: relazioni, sezioni, righe del riepilogo e
|
|
42
46
|
* archiviazione. Il rendering sta in TMMultiMasterDetailDcmtsForm
|
|
43
47
|
*/
|
|
44
|
-
export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose }: UseMultiMasterDetailDcmtsParams) => {
|
|
48
|
+
export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose, canNavigateMaster, onNavigateMaster }: UseMultiMasterDetailDcmtsParams) => {
|
|
45
49
|
isLoading: boolean;
|
|
46
50
|
isSummaryReady: boolean;
|
|
47
51
|
phase: MultiDetailArchivePhase;
|
|
@@ -54,6 +58,10 @@ export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose }: U
|
|
|
54
58
|
backToTypeSelection: () => void;
|
|
55
59
|
isBackLocked: boolean;
|
|
56
60
|
closeModal: () => void;
|
|
61
|
+
isMasterNavigationEnabled: boolean;
|
|
62
|
+
canNavigateMasterPrev: boolean;
|
|
63
|
+
canNavigateMasterNext: boolean;
|
|
64
|
+
navigateMaster: (dir: "next" | "prev") => void;
|
|
57
65
|
sectionsOpen: Record<AccordionSection, boolean>;
|
|
58
66
|
setSectionOpen: (section: AccordionSection, isOpen: boolean) => void;
|
|
59
67
|
openAllSections: () => void;
|
|
@@ -33,7 +33,7 @@ const CLOSED_WAIT_PANEL = {
|
|
|
33
33
|
* Logica dell'archiviazione multipla dei dettagli: relazioni, sezioni, righe del riepilogo e
|
|
34
34
|
* archiviazione. Il rendering sta in TMMultiMasterDetailDcmtsForm
|
|
35
35
|
*/
|
|
36
|
-
export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
36
|
+
export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose, canNavigateMaster, onNavigateMaster }) => {
|
|
37
37
|
const { renderDataListCell, loadDataListsAsync } = useDataListItem();
|
|
38
38
|
const [isLoading, setIsLoading] = useState(true);
|
|
39
39
|
const [phase, setPhase] = useState(MultiDetailArchivePhase.SelectDcmtType);
|
|
@@ -88,13 +88,14 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
88
88
|
// =============================================================================================
|
|
89
89
|
// MASTER
|
|
90
90
|
// =============================================================================================
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
/** Identifica il master: cambia quando la navigazione sposta il modale su un altro documento */
|
|
92
|
+
const masterKey = `${masterDcmt?.TID}_${masterDcmt?.DID}`;
|
|
93
|
+
// Metadati del master indicizzati per mid: si leggono una sola volta per documento e la cache
|
|
94
|
+
// si invalida se cambia il master (TID/DID) o dopo un suo salvataggio
|
|
93
95
|
const masterMetadataCacheRef = useRef(undefined);
|
|
94
96
|
const getMasterMetadataAsync = () => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
masterMetadataCacheRef.current = { key, promise: getDcmtMetadataByMid(masterDcmt) };
|
|
97
|
+
if (masterMetadataCacheRef.current?.key !== masterKey) {
|
|
98
|
+
masterMetadataCacheRef.current = { key: masterKey, promise: getDcmtMetadataByMid(masterDcmt) };
|
|
98
99
|
}
|
|
99
100
|
return masterMetadataCacheRef.current.promise;
|
|
100
101
|
};
|
|
@@ -103,6 +104,21 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
103
104
|
setMasterInfoReloadToken(token => token + 1);
|
|
104
105
|
setIsOpenMasterForm(false);
|
|
105
106
|
};
|
|
107
|
+
// La navigazione ha spostato il modale su un altro master: le righe del riepilogo erano del
|
|
108
|
+
// documento precedente e sono già state scartate con conferma (vedi navigateMaster).
|
|
109
|
+
// Il tipo di dettaglio scelto resta: nella lista di provenienza i documenti sono dello stesso tipo
|
|
110
|
+
const previousMasterKeyRef = useRef(masterKey);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (previousMasterKeyRef.current === masterKey)
|
|
113
|
+
return;
|
|
114
|
+
previousMasterKeyRef.current = masterKey;
|
|
115
|
+
setArchiveRows([]);
|
|
116
|
+
setFocusedRowKey(undefined);
|
|
117
|
+
setSelectedRowKeys([]);
|
|
118
|
+
setIsOpenAddForm(false);
|
|
119
|
+
setEditingRowId(undefined);
|
|
120
|
+
setIsOpenMasterForm(false);
|
|
121
|
+
}, [masterKey]);
|
|
106
122
|
// Associazioni della relazione valorizzate dal master, per pre-compilare il dettaglio
|
|
107
123
|
const getMasterAssociationsForDetailAsync = async (relation) => {
|
|
108
124
|
if (!relation)
|
|
@@ -112,7 +128,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
112
128
|
// =============================================================================================
|
|
113
129
|
// RELAZIONI E STRUTTURA DEL DETTAGLIO
|
|
114
130
|
// =============================================================================================
|
|
115
|
-
// Relazioni di dettaglio archiviabili; con una sola relazione si salta la scelta del tipo
|
|
131
|
+
// Relazioni di dettaglio archiviabili; con una sola relazione si salta la scelta del tipo.
|
|
132
|
+
// Dipende dal tipo del master: la navigazione resta nella stessa lista, quindi di norma non cambia
|
|
116
133
|
useEffect(() => {
|
|
117
134
|
const loadAsync = async () => {
|
|
118
135
|
try {
|
|
@@ -148,7 +165,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
148
165
|
}
|
|
149
166
|
};
|
|
150
167
|
loadAsync();
|
|
151
|
-
}, []);
|
|
168
|
+
}, [dtd?.id]);
|
|
152
169
|
// Colonne della griglia di riepilogo e supporto al file del tipo selezionato
|
|
153
170
|
useEffect(() => {
|
|
154
171
|
const loadDetailStructureAsync = async () => {
|
|
@@ -215,14 +232,15 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
215
232
|
setIsLoadingArchivedDetails(false);
|
|
216
233
|
}
|
|
217
234
|
};
|
|
218
|
-
// I risultati seguono il tipo documento selezionato e
|
|
219
|
-
// nella testata e nella barra dei pannelli deve essere
|
|
235
|
+
// I risultati seguono il tipo documento selezionato e il master (che cambia con la navigazione), e si
|
|
236
|
+
// caricano anche a sezione chiusa: il contatore nella testata e nella barra dei pannelli deve essere
|
|
237
|
+
// corretto senza dover aprire la sezione
|
|
220
238
|
useEffect(() => {
|
|
221
239
|
setArchivedDetailResults([]);
|
|
222
240
|
areArchivedDetailsLoadedRef.current = false;
|
|
223
241
|
if (selectedRelation?.detailTID !== undefined)
|
|
224
242
|
refreshArchivedDetailsAsync();
|
|
225
|
-
}, [selectedRelation?.detailTID]);
|
|
243
|
+
}, [selectedRelation?.detailTID, masterKey]);
|
|
226
244
|
// =============================================================================================
|
|
227
245
|
// SEZIONI
|
|
228
246
|
// =============================================================================================
|
|
@@ -298,6 +316,32 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
298
316
|
onClose(); },
|
|
299
317
|
});
|
|
300
318
|
};
|
|
319
|
+
// Navigazione del master sulla lista di provenienza: senza la sua callback le frecce non si mostrano
|
|
320
|
+
const canNavigateMasterPrev = !!onNavigateMaster && (canNavigateMaster?.('prev') ?? false);
|
|
321
|
+
const canNavigateMasterNext = !!onNavigateMaster && (canNavigateMaster?.('next') ?? false);
|
|
322
|
+
const isMasterNavigationEnabled = !!onNavigateMaster;
|
|
323
|
+
/**
|
|
324
|
+
* Sposta il master sul documento precedente/successivo della lista di provenienza. Le righe già
|
|
325
|
+
* inserite appartengono al master corrente: si perdono, quindi prima si chiede conferma
|
|
326
|
+
*/
|
|
327
|
+
const navigateMaster = (dir) => {
|
|
328
|
+
if (!onNavigateMaster)
|
|
329
|
+
return;
|
|
330
|
+
if (dir === 'prev' ? !canNavigateMasterPrev : !canNavigateMasterNext)
|
|
331
|
+
return;
|
|
332
|
+
if (archiveRows.length === 0) {
|
|
333
|
+
onNavigateMaster(dir);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
TMMessageBoxManager.show({
|
|
337
|
+
title: SDKUI_Localizator.Archive,
|
|
338
|
+
message: SDKUI_Localizator.ChangeMasterWithoutArchiving_Confirm,
|
|
339
|
+
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
340
|
+
// Le righe le azzera l'effetto sul cambio di master, non serve svuotarle qui
|
|
341
|
+
onButtonClick: (button) => { if (button === ButtonNames.YES)
|
|
342
|
+
onNavigateMaster(dir); },
|
|
343
|
+
});
|
|
344
|
+
};
|
|
301
345
|
// =============================================================================================
|
|
302
346
|
// RIGHE DEL RIEPILOGO
|
|
303
347
|
// =============================================================================================
|
|
@@ -694,6 +738,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
694
738
|
isLoading, isSummaryReady, phase, setPhase,
|
|
695
739
|
detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes,
|
|
696
740
|
selectRelation, backToTypeSelection, isBackLocked, closeModal,
|
|
741
|
+
// Navigazione del master sulla lista di provenienza
|
|
742
|
+
isMasterNavigationEnabled, canNavigateMasterPrev, canNavigateMasterNext, navigateMaster,
|
|
697
743
|
// Sezioni
|
|
698
744
|
sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken,
|
|
699
745
|
hasSavedLayout, setHasSavedLayout,
|