@topconsultnpm/sdkui-react 6.22.0-dev2.23 → 6.22.0-dev2.24

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.
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useMemo, useState } from "react";
2
+ 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";
@@ -19,7 +19,7 @@ import TMDcmtForm from "./TMDcmtForm";
19
19
  import TMDcmtIcon from "./TMDcmtIcon";
20
20
  import TMMasterInfoFields from "./TMMasterInfoFields";
21
21
  import { MultiDetailArchivePhase, useMultiMasterDetailDcmts } from "../../../hooks/useMultiMasterDetailDcmts";
22
- import { accordionStyles, getDtdDisplayName, INLINE_EDITABLE_CELL_CLASS, multiDetailStyles, PANEL_GUTTER_PX, renderAccordionHeader, renderSelectDcmtType, renderStepper, SECTION_HEADER_HEIGHT_PX, sectionPanelHeights, TMSectionPanelsHeights, TMSummarySectionSlot } from "./TMMultiMasterDetailDcmtsUtils";
22
+ import { accordionStyles, getDtdDisplayName, INLINE_EDITABLE_CELL_CLASS, multiDetailStyles, PANEL_GUTTER_PX, renderAccordionHeader, renderSelectDcmtType, renderStepper, SECTION_HEADER_HEIGHT_PX, sectionPanelHeights, TMSectionPanelsHeights, TMSummaryResizeTracker, TMSummarySectionSlot } from "./TMMultiMasterDetailDcmtsUtils";
23
23
  // Stili delle sezioni in TMMultiMasterDetailDcmtsUtils (vedi accordionStyles e multiDetailStyles)
24
24
  const styles = {
25
25
  content: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 },
@@ -46,7 +46,7 @@ const styles = {
46
46
  const MOBILE_SECTION = 'archiveList';
47
47
  const TMMultiMasterDetailDcmtsForm = (props) => {
48
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, 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, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
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, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
50
50
  const deviceType = useDeviceType();
51
51
  // Su mobile il panel manager mostra un pannello alla volta: le sezioni si raggiungono dalla sua barra
52
52
  const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
@@ -56,6 +56,16 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
56
56
  if (isMobile && isSummaryReady)
57
57
  openAllSections();
58
58
  }, [isMobile, isSummaryReady]);
59
+ // Le altezze sono una scelta dell'utente solo da quando sposta un separatore: quelle calcolate all'apertura
60
+ // o al collasso di una sezione non vanno salvate. Con un layout già salvato lo sono comunque
61
+ const hasUserResizedRef = useRef(false);
62
+ useEffect(() => { hasUserResizedRef.current = hasMultiDetailPanelStates(); }, [layoutResetToken]);
63
+ const persistPanelStatesOnUserResize = useCallback((panelStates) => {
64
+ if (!hasUserResizedRef.current)
65
+ return;
66
+ saveMultiDetailPanelStates(panelStates);
67
+ setHasSavedLayout(true);
68
+ }, []);
59
69
  // Ricerca nella griglia dei dettagli da archiviare, come in TMSearch (vedi useDocumentOperations)
60
70
  const [showGridSearch, setShowGridSearch] = useState(false);
61
71
  const toggleGridSearchItem = {
@@ -139,14 +149,16 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
139
149
  { name: SDKUI_Localizator.Deletion, icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }), operationType: 'singleRow', beginGroup: true, onClick: (id) => deleteRow(archiveRows.find(r => r.id === id)) },
140
150
  toggleGridSearchItem,
141
151
  ], [archiveRows, showGridSearch]);
142
- // Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo
143
- const optionsMenuItems = useMemo(() => [
144
- { name: 'Cancella layout', icon: _jsx(IconEraser, { fontSize: 18, color: TMColors.error }), onClick: clearLayout },
145
- ], [clearLayout]);
152
+ // Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo.
153
+ // Con il layout di default non c'è niente da cancellare, quindi la voce non compare
154
+ const optionsMenuItems = useMemo(() => hasSavedLayout
155
+ ? [{ name: 'Cancella layout', icon: _jsx(IconEraser, { fontSize: 18, color: TMColors.error }), onClick: clearLayout }]
156
+ : [], [clearLayout, hasSavedLayout]);
146
157
  // Le opzioni riguardano i pannelli del riepilogo: fuori da quella fase non hanno effetto, e su mobile
147
158
  // non c'è un layout da cancellare (i pannelli si vedono uno alla volta, a tutta altezza)
148
159
  const renderOptionsMenu = () => {
149
- if (!isSummaryPhase || isMobile)
160
+ // Senza voci non si mostra il pulsante, per non aprire un menu vuoto
161
+ if (!isSummaryPhase || isMobile || optionsMenuItems.length === 0)
150
162
  return undefined;
151
163
  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" }) }) }) }));
152
164
  };
@@ -245,7 +257,9 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
245
257
  // passaggio fra impilato e un pannello alla volta, i pannelli vanno rimontati
246
258
  , { panels: panels, initialVisibility: initialVisibility, defaultDimensions: initialDimensions, initialDimensions: initialDimensions, initialMobilePanelId: MOBILE_SECTION,
247
259
  // Su mobile il layout è imposto dal dispositivo: salvarlo sovrascriverebbe quello dell'utente
248
- isPersistenceEnabled: !isMobile, persistPanelStates: !isMobile ? saveMultiDetailPanelStates : undefined, children: [_jsx(TMSectionPanelsHeights, { sections: sections, sectionsOpen: sectionsOpen }), _jsx(TMPanelManagerContainer, { panels: panels, direction: 'vertical', showToolbar: isMobile, minPanelSizePx: SECTION_HEADER_HEIGHT_PX, gutterSizePx: isMobile ? undefined : PANEL_GUTTER_PX })] }, `${layoutResetToken}-${isMobile}`) }) }));
260
+ isPersistenceEnabled: !isMobile,
261
+ // Le altezze si salvano solo dopo che l'utente ha spostato un separatore (vedi TMSummaryResizeTracker)
262
+ persistPanelStates: !isMobile ? persistPanelStatesOnUserResize : undefined, children: [_jsx(TMSummaryResizeTracker, { onUserResize: () => { hasUserResizedRef.current = true; } }), _jsx(TMSectionPanelsHeights, { sections: sections, sectionsOpen: sectionsOpen }), _jsx(TMPanelManagerContainer, { panels: panels, direction: 'vertical', showToolbar: isMobile, minPanelSizePx: SECTION_HEADER_HEIGHT_PX, gutterSizePx: isMobile ? undefined : PANEL_GUTTER_PX })] }, `${layoutResetToken}-${isMobile}`) }) }));
249
263
  };
250
264
  const renderArchiveSummary = () => (_jsxs("div", { style: styles.summaryContainer, children: [renderSectionsInPanels(), _jsxs("div", { style: styles.footer, children: [_jsxs("label", { style: styles.footerLabel, ...hoverStyle({ borderColor: TMColors.primary, color: TMColors.text_normal }, { borderColor: TMColors.card_background, color: TMColors.label_normal }), children: [_jsx("input", { type: "checkbox", checked: stopOnFirstError, onChange: (e) => setStopOnFirstError(e.target.checked), style: styles.checkbox }), SDKUI_Localizator.StopOnFirstError] }), _jsxs("button", { type: "button", onClick: archiveAllAsync, disabled: isArchiveDisabled, style: {
251
265
  ...styles.archiveButton,
@@ -343,6 +343,15 @@ export declare const TMSummarySectionSlot: ({ section, render, onBeforeMaximize
343
343
  render: (panel: SectionPanelState) => React.ReactNode;
344
344
  onBeforeMaximize?: (section: AccordionSection) => void;
345
345
  }) => import("react/jsx-runtime").JSX.Element;
346
+ /**
347
+ * Segnala che l'utente ha spostato un separatore: è l'unico caso in cui le altezze dei pannelli sono
348
+ * una sua scelta, quindi la sola da salvare. Le quote calcolate all'apertura del riepilogo o al
349
+ * collasso di una sezione non vanno scritte nel local storage.
350
+ * Vive dentro il provider perché il contesto è leggibile solo lì
351
+ */
352
+ export declare const TMSummaryResizeTracker: ({ onUserResize }: {
353
+ onUserResize: () => void;
354
+ }) => null;
346
355
  /**
347
356
  * Tiene allineate le altezze dei pannelli al collasso delle sezioni: il panel manager non
348
357
  * conosce il collasso, quindi è qui che l'apertura di una sezione si traduce nelle nuove quote.
@@ -9,7 +9,11 @@ import TMTooltip from '../../base/TMTooltip';
9
9
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
10
10
  import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
11
11
  // l'elenco da archiviare è l'informazione più importante, quindi prende la fetta maggiore
12
- export const SECTION_WEIGHTS = { masterInfo: 25, archivedDetails: 30, archiveList: 45 };
12
+ export const SECTION_WEIGHTS = {
13
+ masterInfo: 25,
14
+ archivedDetails: 30,
15
+ archiveList: 45
16
+ };
13
17
  export const SECTION_HEADER_HEIGHT_PX = 30;
14
18
  // Spessore dei separatori fra i pannelli
15
19
  export const PANEL_GUTTER_PX = 10;
@@ -209,6 +213,20 @@ export const TMSummarySectionSlot = ({ section, render, onBeforeMaximize }) => {
209
213
  };
210
214
  return _jsx(_Fragment, { children: render({ isMaximized, canMaximize: !isMobile, canCollapse: !isMobile, toggleMaximize: toggleMaximizeSection }) });
211
215
  };
216
+ /**
217
+ * Segnala che l'utente ha spostato un separatore: è l'unico caso in cui le altezze dei pannelli sono
218
+ * una sua scelta, quindi la sola da salvare. Le quote calcolate all'apertura del riepilogo o al
219
+ * collasso di una sezione non vanno scritte nel local storage.
220
+ * Vive dentro il provider perché il contesto è leggibile solo lì
221
+ */
222
+ export const TMSummaryResizeTracker = ({ onUserResize }) => {
223
+ const { isResizingActive } = useTMPanelManagerContext();
224
+ useEffect(() => {
225
+ if (isResizingActive)
226
+ onUserResize();
227
+ }, [isResizingActive]);
228
+ return null;
229
+ };
212
230
  /**
213
231
  * Tiene allineate le altezze dei pannelli al collasso delle sezioni: il panel manager non
214
232
  * conosce il collasso, quindi è qui che l'apertura di una sezione si traduce nelle nuove quote.
@@ -53,7 +53,6 @@ export const TMPanelManagerProvider = (props) => {
53
53
  // Recursively hide parents
54
54
  const parentsToHide = hideParentRecursively(id, hierarchyMap, updatedVisibility);
55
55
  parentsToHide.forEach(([pid, visible]) => { updatedVisibility[pid] = visible; });
56
- 4;
57
56
  // Redistribute dimensions after hiding
58
57
  setPanelDimensions(prev => redistributeDimensionsOnHide(id, prev, hierarchyMap, updatedVisibility));
59
58
  }
@@ -133,15 +133,18 @@ export type MultiDetailPanelStates = Record<string, MultiDetailPanelState>;
133
133
  export declare class ArchivingSettings {
134
134
  mruTIDs: number[];
135
135
  defaultTree: number;
136
- /** Sezioni collassate dall'utente nel riepilogo, ripristinate alla riapertura */
137
- multiDetailSectionsOpen: MultiDetailSectionsOpen;
136
+ /**
137
+ * Sezioni collassate dall'utente nel riepilogo, ripristinate alla riapertura. Assente finché
138
+ * l'utente non ne chiude una: le impostazioni di default non vanno scritte nel local storage
139
+ */
140
+ multiDetailSectionsOpen?: MultiDetailSectionsOpen;
138
141
  /**
139
142
  * Altezze dei pannelli del riepilogo, gestite dal panel manager. Non comprende il collasso delle
140
- * sezioni, che è in multiDetailSectionsOpen: resta vuoto finché l'utente non sposta un separatore.
143
+ * sezioni, che è in multiDetailSectionsOpen: assente finché l'utente non sposta un separatore.
141
144
  */
142
- multiDetailPanelStates: MultiDetailPanelStates;
145
+ multiDetailPanelStates?: MultiDetailPanelStates;
143
146
  }
144
- /** Stato di apertura salvato delle sezioni: i default coprono le impostazioni scritte prima di questo campo */
147
+ /** Stato di apertura salvato delle sezioni: i default coprono l'impostazione mai modificata dall'utente */
145
148
  export declare const getMultiDetailSectionsOpen: () => MultiDetailSectionsOpen;
146
149
  export declare const saveMultiDetailSectionsOpen: (sectionsOpen: MultiDetailSectionsOpen) => void;
147
150
  /** Stato salvato dei pannelli: vuoto finché l'utente non ne ha ridimensionato nessuno */
@@ -149,6 +152,8 @@ export declare const getMultiDetailPanelStates: () => MultiDetailPanelStates;
149
152
  /** Il panel manager applica lo stato salvato solo se esiste: altrimenti riparte dalle quote di default */
150
153
  export declare const hasMultiDetailPanelStates: () => boolean;
151
154
  export declare const saveMultiDetailPanelStates: (panelStates: MultiDetailPanelStates) => void;
155
+ /** C'è un layout dell'utente da cancellare: nel local storage le due impostazioni ci sono solo se diverse dai default */
156
+ export declare const hasMultiDetailLayout: () => boolean;
152
157
  /**
153
158
  * Elimina dal local storage il layout configurato dall'utente per il riepilogo: altezze dei pannelli
154
159
  * e sezioni collassate. Alla riapertura le sezioni sono tutte espanse con le quote di default.
@@ -198,23 +198,19 @@ export class ArchivingSettings {
198
198
  constructor() {
199
199
  this.mruTIDs = [];
200
200
  this.defaultTree = -1;
201
- /** Sezioni collassate dall'utente nel riepilogo, ripristinate alla riapertura */
202
- this.multiDetailSectionsOpen = { ...DEFAULT_MULTI_DETAIL_SECTIONS_OPEN };
203
- /**
204
- * Altezze dei pannelli del riepilogo, gestite dal panel manager. Non comprende il collasso delle
205
- * sezioni, che è in multiDetailSectionsOpen: resta vuoto finché l'utente non sposta un separatore.
206
- */
207
- this.multiDetailPanelStates = {};
208
201
  }
209
202
  }
210
- /** Stato di apertura salvato delle sezioni: i default coprono le impostazioni scritte prima di questo campo */
203
+ /** Stato di apertura salvato delle sezioni: i default coprono l'impostazione mai modificata dall'utente */
211
204
  export const getMultiDetailSectionsOpen = () => ({
212
205
  ...DEFAULT_MULTI_DETAIL_SECTIONS_OPEN,
213
206
  ...SDKUI_Globals.userSettings.archivingSettings?.multiDetailSectionsOpen,
214
207
  });
215
208
  export const saveMultiDetailSectionsOpen = (sectionsOpen) => {
216
- // Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero
217
- SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = { ...sectionsOpen };
209
+ const isDefault = Object.keys(DEFAULT_MULTI_DETAIL_SECTIONS_OPEN)
210
+ .every(section => sectionsOpen[section] === DEFAULT_MULTI_DETAIL_SECTIONS_OPEN[section]);
211
+ // Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero.
212
+ // Tornata ai default l'impostazione va rimossa, così nel local storage restano solo le modifiche dell'utente
213
+ SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = isDefault ? undefined : { ...sectionsOpen };
218
214
  };
219
215
  /** Stato salvato dei pannelli: vuoto finché l'utente non ne ha ridimensionato nessuno */
220
216
  export const getMultiDetailPanelStates = () => SDKUI_Globals.userSettings.archivingSettings?.multiDetailPanelStates ?? {};
@@ -226,14 +222,17 @@ export const saveMultiDetailPanelStates = (panelStates) => {
226
222
  // Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero
227
223
  SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = { ...panelStates };
228
224
  };
225
+ /** C'è un layout dell'utente da cancellare: nel local storage le due impostazioni ci sono solo se diverse dai default */
226
+ export const hasMultiDetailLayout = () => hasMultiDetailPanelStates() || SDKUI_Globals.userSettings.archivingSettings?.multiDetailSectionsOpen !== undefined;
229
227
  /**
230
228
  * Elimina dal local storage il layout configurato dall'utente per il riepilogo: altezze dei pannelli
231
229
  * e sezioni collassate. Alla riapertura le sezioni sono tutte espanse con le quote di default.
232
230
  */
233
231
  export const clearMultiDetailLayout = () => {
234
- // Il proxy persiste solo intercettando l'assegnazione di una proprietà: entrambe le mappe vanno riassegnate
235
- SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = {};
236
- SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = { ...DEFAULT_MULTI_DETAIL_SECTIONS_OPEN };
232
+ // Il proxy persiste solo intercettando l'assegnazione di una proprietà: entrambi i campi vanno riassegnati.
233
+ // Senza valore spariscono dal local storage e alla riapertura tornano validi i default
234
+ SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = undefined;
235
+ SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = undefined;
237
236
  };
238
237
  export class FullTextSettings {
239
238
  constructor() {
@@ -59,6 +59,8 @@ export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose }: U
59
59
  openAllSections: () => void;
60
60
  clearLayout: () => void;
61
61
  layoutResetToken: number;
62
+ hasSavedLayout: boolean;
63
+ setHasSavedLayout: import("react").Dispatch<import("react").SetStateAction<boolean>>;
62
64
  canUpdateMaster: boolean;
63
65
  masterFieldsCount: number;
64
66
  getMasterMetadataAsync: () => Promise<Map<number, MetadataScalarValue>>;
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { ArchiveConstraints, ArchiveEngineByID, MetadataDataDomains, MetadataDataTypes, RelationCacheService, RelationTypes, ResultTypes, SDK_Globals } from '@topconsultnpm/sdk-ts';
3
3
  import { MetadataValueDescriptorEx } from '../ts';
4
- import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
4
+ import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, hasMultiDetailLayout, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
5
5
  import { getDcmtMetadataByMid, getUserMetadataSorted, mapAssociationsFromMetadata } from '../helper/dcmtsHelper';
6
6
  import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../components/base/TMPopUp';
7
7
  import TMSpinner from '../components/base/TMSpinner';
@@ -54,6 +54,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
54
54
  const [sectionsOpen, setSectionsOpen] = useState(getMultiDetailSectionsOpen());
55
55
  // Cambia a ogni cancellazione del layout, per rimontare i pannelli sulle dimensioni di default
56
56
  const [layoutResetToken, setLayoutResetToken] = useState(0);
57
+ // Layout dell'utente nel local storage: senza non c'è niente da cancellare (è uno stato perché le altezze le salva il panel manager)
58
+ const [hasSavedLayout, setHasSavedLayout] = useState(hasMultiDetailLayout());
57
59
  // Master
58
60
  const [isOpenMasterForm, setIsOpenMasterForm] = useState(false);
59
61
  const [masterInfoReloadToken, setMasterInfoReloadToken] = useState(0);
@@ -222,6 +224,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
222
224
  const next = { ...sectionsOpen, [section]: isOpen };
223
225
  setSectionsOpen(next);
224
226
  saveMultiDetailSectionsOpen(next);
227
+ // Riaperte tutte le sezioni il collasso torna ai default: senza altezze salvate non c'è più layout
228
+ setHasSavedLayout(hasMultiDetailLayout());
225
229
  // I dettagli già archiviati si caricano alla prima apertura della sezione
226
230
  if (section === 'archivedDetails' && isOpen && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
227
231
  refreshArchivedDetailsAsync();
@@ -254,6 +258,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
254
258
  const restored = getMultiDetailSectionsOpen();
255
259
  setSectionsOpen(restored);
256
260
  setLayoutResetToken(prev => prev + 1);
261
+ setHasSavedLayout(false);
257
262
  // Le sezioni riaperte dal ripristino caricano i dettagli già archiviati come alla prima apertura
258
263
  if (restored.archivedDetails && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
259
264
  refreshArchivedDetailsAsync();
@@ -562,6 +567,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
562
567
  selectRelation, backToTypeSelection, isBackLocked, closeModal,
563
568
  // Sezioni
564
569
  sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken,
570
+ hasSavedLayout, setHasSavedLayout,
565
571
  // Master
566
572
  canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken,
567
573
  isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.22.0-dev2.23",
3
+ "version": "6.22.0-dev2.24",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",