@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.
@@ -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: renderOptionsMenu(), children: _jsxs("div", { style: styles.content, children: [hasMultipleTypes && _jsx("div", { style: styles.header, children: renderStepper({
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
- // Metadati del master indicizzati per mid: il master è fisso, quindi si legge una sola volta
92
- // e la cache si invalida solo se cambia il documento (TID/DID) o dopo un salvataggio
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
- const key = `${masterDcmt?.TID}_${masterDcmt?.DID}`;
96
- if (masterMetadataCacheRef.current?.key !== key) {
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 si caricano anche a sezione chiusa: il contatore
219
- // nella testata e nella barra dei pannelli deve essere corretto senza dover aprire la sezione
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.22.0-dev2.39",
3
+ "version": "6.22.0-dev2.40",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",