@topconsultnpm/sdkui-react 6.22.0-dev2.19 → 6.22.0-dev2.20

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.
Files changed (41) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
  2. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
  3. package/lib/components/base/TMDataGrid.d.ts +2 -0
  4. package/lib/components/base/TMDataGrid.js +68 -4
  5. package/lib/components/base/TMModal.d.ts +4 -0
  6. package/lib/components/base/TMModal.js +42 -11
  7. package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
  8. package/lib/components/features/documents/TMDcmtForm.js +94 -40
  9. package/lib/components/features/documents/TMMasterDetailDcmts.js +88 -30
  10. package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
  11. package/lib/components/features/documents/TMMasterInfoFields.js +172 -0
  12. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
  13. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +256 -0
  14. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +352 -0
  15. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +221 -0
  16. package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
  17. package/lib/components/features/documents/TMRelationViewer.js +69 -20
  18. package/lib/components/features/search/TMSearchResult.d.ts +8 -0
  19. package/lib/components/features/search/TMSearchResult.js +131 -23
  20. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  21. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
  22. package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
  23. package/lib/components/layout/panelManager/types.d.ts +3 -0
  24. package/lib/helper/SDKUI_Globals.d.ts +34 -0
  25. package/lib/helper/SDKUI_Globals.js +37 -0
  26. package/lib/helper/SDKUI_Localizator.d.ts +18 -0
  27. package/lib/helper/SDKUI_Localizator.js +180 -0
  28. package/lib/helper/TMIcons.d.ts +1 -0
  29. package/lib/helper/TMIcons.js +3 -0
  30. package/lib/helper/dcmtsHelper.d.ts +20 -0
  31. package/lib/helper/dcmtsHelper.js +58 -1
  32. package/lib/helper/helpers.d.ts +9 -0
  33. package/lib/helper/helpers.js +8 -0
  34. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  35. package/lib/hooks/useArchiveListForm.js +46 -0
  36. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  37. package/lib/hooks/useDocumentOperations.js +16 -4
  38. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +97 -0
  39. package/lib/hooks/useMultiMasterDetailDcmts.js +555 -0
  40. package/lib/hooks/useRelatedDocuments.js +2 -26
  41. package/package.json +1 -1
@@ -500,6 +500,6 @@ const TMContextMenu = ({ items, trigger = 'right', children, target, externalCon
500
500
  // display: 'inline-block',
501
501
  WebkitTouchCallout: isIOS ? 'none' : undefined,
502
502
  WebkitUserSelect: isIOS ? 'none' : undefined,
503
- }, children: children })), menuState.visible && createPortal(_jsxs(_Fragment, { children: [_jsxs(S.MenuContainer, { ref: menuRef, "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, "$needsScroll": needsScroll, "$maxHeight": maxHeight, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", "data-open-up": submenu.openUp ? 'true' : 'false', onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }), document.body)] }));
503
+ }, children: children })), menuState.visible && createPortal(_jsxs(_Fragment, { children: [_jsxs(S.MenuContainer, { ref: menuRef, className: "notranslate", translate: "no", "$x": menuState.position.x, "$y": menuState.position.y, "$openLeft": openLeft, "$openUp": openUp, "$isPositioned": isCalculated, "$externalControl": !!externalControl, "$needsScroll": needsScroll, "$maxHeight": maxHeight, children: [isMobile && menuState.parentNames.length > 0 && (_jsxs(S.MobileMenuHeader, { children: [_jsx(S.BackButton, { onClick: handleBack, "aria-label": "Go back", children: _jsx(IconArrowLeft, {}) }), _jsx(S.HeaderTitle, { children: currentParentName })] })), renderMenuItems(currentMenu, 0)] }), !isMobile && hoveredSubmenus.map((submenu, idx) => (_jsx(S.Submenu, { className: "notranslate", translate: "no", "$parentRect": submenu.parentRect, "$openUp": submenu.openUp, "data-submenu": "true", "data-open-up": submenu.openUp ? 'true' : 'false', onMouseEnter: handleSubmenuMouseEnter, onMouseLeave: () => handleMouseLeave(submenu.depth), children: renderMenuItems(submenu.items, submenu.depth) }, `submenu-${submenu.depth}-${idx}`)))] }), document.body)] }));
504
504
  };
505
505
  export default TMContextMenu;
@@ -837,7 +837,7 @@ const TMFloatingMenuBar = ({ containerRef, contextMenuItems = [], isConstrained
837
837
  setState(s => ({ ...s, draggedItemIndex: null }));
838
838
  setDragOverIndex(null);
839
839
  };
840
- return (_jsxs(S.FloatingContainer, { ref: floatingRef, "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": false, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: undefined, children: [_jsx(ContextMenu, { items: [
840
+ return (_jsxs(S.FloatingContainer, { ref: floatingRef, className: "notranslate", translate: "no", "$x": pixelPosition.x, "$y": pixelPosition.y, "$orientation": state.orientation, "$verticalDirection": state.verticalDirection, "$isDragging": state.isDragging, "$isConfigMode": false, "$isConstrained": isConstrained, "$isHidden": isOrientationChanging, "$bgColor": bgColor, onContextMenu: undefined, children: [_jsx(ContextMenu, { items: [
841
841
  {
842
842
  name: state.orientation === 'horizontal' ? 'Floating bar verticale' : 'Floating bar orizzontale',
843
843
  icon: _jsx(IconRotate, { fontSize: 16, style: { transform: state.orientation === 'horizontal' ? 'rotate(90deg)' : 'rotate(0deg)' } }),
@@ -46,6 +46,8 @@ export interface TMDataGridProps<T> extends IDataGridOptions {
46
46
  showSearchPanel?: boolean;
47
47
  /** Show the group panel */
48
48
  showGroupPanel?: boolean;
49
+ /** Rende il paginatore compatto (meno alto, font e pulsanti ridotti, page numbers sostituiti da "pagina / totale"): utile quando la griglia è dentro pannelli bassi */
50
+ compactPager?: boolean;
49
51
  /** Counter configuration */
50
52
  counterConfig?: ITMCounterContainerProps;
51
53
  /** Master Detail */
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
2
+ import React, { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
3
+ import styled, { css } from 'styled-components';
3
4
  import DataGrid, { Column, HeaderFilter, Selection, Scrolling, LoadPanel, SearchPanel, Pager, Sorting, Paging, FilterPanel, ColumnChooser, Grouping, GroupPanel, Summary, Editing, FilterRow, StateStoring, RowDragging, MasterDetail, Position, ColumnChooserSearch, ColumnChooserSelection } from 'devextreme-react/data-grid';
4
5
  import DataSource from 'devextreme/data/data_source';
5
6
  import { IconAll, IconSelected, IconVisible, SDKUI_Globals, SDKUI_Localizator } from '../../helper';
@@ -18,7 +19,7 @@ const TMDataGrid = React.forwardRef((props, ref) => {
18
19
  // main properties
19
20
  keyExpr = 'id', dataSource, focusedRowEnabled = true, hoverStateEnabled = true, focusedRowKey, selectedRowKeys = [],
20
21
  // custom options
21
- dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showHeaderColumnChooser = false, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusTrigger = 0, counterConfig = { show: false, items: new Map() }, onHasFiltersChange, customContextMenuItems,
22
+ dataColumns = [], pageSize = TMDataGridPageSize.Large, showHeaderFilter = true, showFilterPanel = true, showHeaderColumnChooser = false, showLoadPanel = true, showSearchPanel = true, searchPanelToolbarPosition = 'before', searchPanelFocusTrigger = 0, counterConfig = { show: false, items: new Map() }, compactPager = false, onHasFiltersChange, customContextMenuItems,
22
23
  // events and callbacks
23
24
  onSelectionChanged, onFocusedRowChanged, onRowDblClick, onRowClick, onCellClick, onCellDblClick, onOptionChanged, onContentReady, onContextMenuPreparing, onInitialized, onEditorPreparing, onCellPrepared, onRowPrepared, onRowUpdating, onRowExpanded, onRowCollapsed, onRowUpdated, onSaved, onEditCanceled, onEditingStart, onEditingChange, customizeColumns, onKeyDown, scrolling = { mode: 'standard', useNative: SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar === 1 }, paging = { enabled: true, pageSize: pageSize }, pager = { visible: true, showInfo: true, showNavigationButtons: true }, selection = { mode: 'multiple', showCheckBoxesMode: "always", selectAllMode: "allPages" }, sorting, summary, stateStoring, grouping, groupPanel, filterRow, headerFilter, editing, rowDragging, masterDetail,
24
25
  // other properties
@@ -77,6 +78,13 @@ const TMDataGrid = React.forwardRef((props, ref) => {
77
78
  setCustomContextMenuRowKey(rowKey);
78
79
  }
79
80
  else {
81
+ // Right-click su spazio vuoto: ricade sulla riga focalizzata solo se esiste ancora.
82
+ // Dopo un'eliminazione focusedRowKey può puntare a una riga non più presente:
83
+ // in tal caso non si mostra il menu (evita di riproporre le azioni della riga eliminata)
84
+ const grid = internalRef.current?.instance();
85
+ const focusedRowExists = focusedRowKey !== undefined && !!grid && grid.getRowIndexByKey(focusedRowKey) >= 0;
86
+ if (!focusedRowExists)
87
+ return;
80
88
  setCustomContextMenuRowKey(focusedRowKey);
81
89
  }
82
90
  setCustomContextMenuVisible(true);
@@ -358,7 +366,14 @@ const TMDataGrid = React.forwardRef((props, ref) => {
358
366
  // Propaga l'evento originale
359
367
  onOptionChanged?.(e);
360
368
  }, [onOptionChanged, onHasFiltersChange]);
361
- return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [_jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
369
+ // In modalità compatta il pager usa il displayMode 'compact' di DevExtreme (page numbers sostituiti da "pagina / totale"),
370
+ // salvo displayMode esplicito passato dal chiamante
371
+ const effectivePager = useMemo(() => {
372
+ if (!pager || !compactPager)
373
+ return pager;
374
+ return { ...pager, displayMode: pager.displayMode ?? 'compact' };
375
+ }, [pager, compactPager]);
376
+ return _jsxs(StyledDataGridContainer, { "$compactPager": compactPager, style: { width: "100%", height: "100%" }, children: [_jsx("div", { ref: gridContainerRef, style: { width: "100%", height: counterConfig.show ? "calc(100% - 25px)" : "100%" }, children: _jsxs(DataGrid, { ref: internalRef, id: id, className: `tm-datagrid ${hasFilters ? 'has-filters' : ''}`,
362
377
  // main properties
363
378
  keyExpr: keyExpr, dataSource: dataSource, selectedRowKeys: selectedRowKeys, focusedRowEnabled: focusedRowEnabled, hoverStateEnabled: hoverStateEnabled,
364
379
  // events and callbacks
@@ -371,13 +386,62 @@ const TMDataGrid = React.forwardRef((props, ref) => {
371
386
  // Temporary: iOS-specific styles to prevent default long-press menu and text selection
372
387
  WebkitTouchCallout: 'none',
373
388
  WebkitUserSelect: 'none'
374
- }, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), pager && _jsx(Pager, { ...pager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { target: ".dx-data-row", items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
389
+ }, children: [dataColumns.map((column, index) => (_jsx(Column, { ...column }, column.caption + index.toString()))), sorting && _jsx(Sorting, { ...sorting }), selection && _jsx(Selection, { ...selection }), scrolling && _jsx(Scrolling, { ...scrolling }), summary && _jsx(Summary, { ...summary }), showHeaderFilter && _jsx(HeaderFilter, { visible: true, ...headerFilter }), rowDragging && _jsx(RowDragging, { ...rowDragging }), filterRow && _jsx(FilterRow, { ...filterRow }), showFilterPanel && _jsx(FilterPanel, { visible: true }), showHeaderColumnChooser && _jsxs(ColumnChooser, { height: "400px", enabled: !showHeaderColumnChooser, mode: "select", children: [_jsx(Position, { my: "center", at: "center", of: window }), _jsx(ColumnChooserSearch, { enabled: true }), _jsx(ColumnChooserSelection, { allowSelectAll: false, selectByClick: true, recursive: true })] }), stateStoring && _jsx(StateStoring, { ...stateStoring }), groupPanel && _jsx(GroupPanel, { ...groupPanel }), _jsx(Grouping, { contextMenuEnabled: true, ...grouping }), _jsx(LoadPanel, { enabled: showLoadPanel }), _jsx(SearchPanel, { visible: showSearchPanel, searchVisibleColumnsOnly: true, highlightSearchText: true }), editing && _jsx(Editing, { ...editing }), paging && _jsx(Paging, { ...paging }), effectivePager && _jsx(Pager, { ...effectivePager, visible: totalRecordCount > pageSize }), masterDetail && _jsx(MasterDetail, { ...masterDetail })] }) }), counterConfig.show && _jsx("div", { style: { width: "100%", height: "25px", display: "flex", alignItems: "center", gap: "15px", backgroundColor: "#e0e0e0" }, children: _jsx(TMCounterContainer, { items: counterValues, bgColorContainer: counterConfig.bgColorContainer, bgColorItem: counterConfig.bgColorItem, hoverColorItem: counterConfig.hoverColorItem, textColorItem: counterConfig.textColorItem }) }), customContextMenuItems && (_jsx(TMContextMenu, { target: ".dx-data-row", items: processCustomContextMenuItems(customContextMenuItems, customContextMenuRowKey), externalControl: {
375
390
  visible: customContextMenuVisible,
376
391
  position: customContextMenuPosition,
377
392
  onClose: () => setCustomContextMenuVisible(false)
378
393
  } }))] });
379
394
  });
380
395
  export default TMDataGrid;
396
+ // Contenitore della griglia: in modalità compatta riduce l'ingombro verticale del pager DevExtreme
397
+ // (padding, altezza minima, font e dimensione dei pulsanti di pagina).
398
+ // Con compactPager = false non emette nessuna regola: width/height restano inline come in precedenza,
399
+ // così il rendering è identico a quello del div originale.
400
+ const StyledDataGridContainer = styled.div `
401
+ ${({ $compactPager }) => $compactPager && css `
402
+ & .dx-datagrid-pager {
403
+ padding: 2px 4px !important;
404
+ min-height: 0 !important;
405
+ font-size: 11px;
406
+ }
407
+
408
+ & .dx-datagrid-pager .dx-page,
409
+ & .dx-datagrid-pager .dx-page-size,
410
+ & .dx-datagrid-pager .dx-selection,
411
+ & .dx-datagrid-pager .dx-navigate-button {
412
+ min-width: 18px !important;
413
+ min-height: 18px !important;
414
+ height: auto !important;
415
+ padding: 1px 5px !important;
416
+ line-height: 16px !important;
417
+ font-size: 11px;
418
+ }
419
+
420
+ & .dx-datagrid-pager .dx-pages,
421
+ & .dx-datagrid-pager .dx-page-sizes,
422
+ & .dx-datagrid-pager .dx-info,
423
+ & .dx-datagrid-pager .dx-page-index,
424
+ & .dx-datagrid-pager .dx-pages .dx-info {
425
+ font-size: 11px;
426
+ margin-top: 0 !important;
427
+ margin-bottom: 0 !important;
428
+ }
429
+
430
+ & .dx-datagrid-pager .dx-info {
431
+ margin-left: 4px !important;
432
+ margin-right: 4px !important;
433
+ }
434
+
435
+ /* Editor del numero di pagina (displayMode compact) */
436
+ & .dx-datagrid-pager .dx-page-index .dx-texteditor-input,
437
+ & .dx-datagrid-pager .dx-page-sizes .dx-texteditor-input {
438
+ min-height: 18px !important;
439
+ height: 18px !important;
440
+ padding: 0 4px !important;
441
+ font-size: 11px;
442
+ }
443
+ `}
444
+ `;
381
445
  const getRecordCount = (dataSource) => {
382
446
  if (Array.isArray(dataSource)) {
383
447
  return dataSource.length; // For array
@@ -14,6 +14,10 @@ interface ITMModal {
14
14
  askClosingConfirm?: boolean;
15
15
  showCloseButton?: boolean;
16
16
  showHeader?: boolean;
17
+ /** Se true, ricentra il popup ogni volta che cambiano width/height, annullando lo spostamento manuale */
18
+ recenterOnResize?: boolean;
19
+ /** Contenuto aggiuntivo nella barra del titolo, prima del pulsante di chiusura (es. un menu di opzioni) */
20
+ titleActions?: React.ReactNode;
17
21
  }
18
22
  declare const TMModal: React.FC<ITMModal>;
19
23
  export default TMModal;
@@ -1,10 +1,13 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useRef } from 'react';
3
3
  import { Popup } from 'devextreme-react';
4
+ import { ToolbarItem } from 'devextreme-react/popup';
4
5
  import styled from 'styled-components';
5
6
  import { TMCard } from './TMLayout';
6
7
  import { FontSize, TMColors } from '../../utils/theme';
7
8
  import { IconWindowMaximize, IconWindowMinimize, svgToString } from '../../helper';
9
+ /** Valori di `height` con cui il popup si dimensiona sul contenuto (vedi applyContentFillLayout) */
10
+ const CONTENT_DRIVEN_HEIGHTS = ['auto', 'max-content', 'min-content', 'fit-content'];
8
11
  const StyledModal = styled.div `
9
12
  width: ${props => props.$width};
10
13
  height: ${props => props.$height};
@@ -39,10 +42,13 @@ const StyledModalContext = styled.div `
39
42
  overflow: auto;
40
43
  height: 100%;
41
44
  `;
42
- const TMModal = ({ resizable = true, expandable = false, isModal = true, title = '', toolbar, onClose, children, width = '100%', height = '100%', fontSize = FontSize.defaultFontSize, hidePopup = true, askClosingConfirm = false, showCloseButton = true, showHeader = true }) => {
45
+ const TMModal = ({ resizable = true, expandable = false, isModal = true, title = '', toolbar, onClose, children, width = '100%', height = '100%', fontSize = FontSize.defaultFontSize, hidePopup = true, askClosingConfirm = false, showCloseButton = true, showHeader = true, recenterOnResize = false, titleActions }) => {
43
46
  const popupRef = useRef(null);
44
47
  const [initialWidth, setInitialWidth] = useState(width);
45
48
  const [initialHeight, setInitialHeight] = useState(height);
49
+ // Posizione controllata: un nuovo riferimento oggetto forza DevExtreme a riposizionare (ricentrare).
50
+ // Lasciata invariata durante il drag, così lo spostamento manuale viene preservato
51
+ const [position, setPosition] = useState(undefined);
46
52
  const [showPopup, setShowPopup] = useState(false);
47
53
  const [isResizing, setIsResizing] = useState(false);
48
54
  const [isFullScreen, setIsFullScreen] = useState(false);
@@ -51,6 +57,26 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
51
57
  const DEFAULT_MIN_HEIGHT = 400;
52
58
  const [minWidth, setMinWidth] = useState(0);
53
59
  const [minHeight, setMinHeight] = useState(0);
60
+ /**
61
+ * DevExtreme fissa in px l'altezza del contenuto del popup: se le dimensioni cambiano a popup
62
+ * già aperto (es. cambio step) resta quella del passo precedente e sotto rimane spazio vuoto.
63
+ * Come figlio flex il contenuto riempie il popup senza dipendere da quel calcolo.
64
+ * Non si applica alle altezze dimensionate sul contenuto (`auto`, `max-content`, ...):
65
+ * lì è il contenuto a dare l'altezza al popup e non c'è spazio da riempire.
66
+ */
67
+ const applyContentFillLayout = () => {
68
+ const content = popupRef.current?.instance()?.content();
69
+ const overlayContent = content?.parentElement;
70
+ if (!content || !overlayContent)
71
+ return;
72
+ const fill = !CONTENT_DRIVEN_HEIGHTS.some(value => initialHeight.trim().startsWith(value));
73
+ overlayContent.style.display = fill ? 'flex' : '';
74
+ overlayContent.style.flexDirection = fill ? 'column' : '';
75
+ content.style.flex = fill ? '1 1 auto' : '';
76
+ // Testata e toolbar inferiore restano alla loro altezza naturale
77
+ overlayContent.querySelectorAll(':scope > .dx-popup-title, :scope > .dx-popup-bottom')
78
+ .forEach(el => { el.style.flexShrink = fill ? '0' : ''; });
79
+ };
54
80
  /**
55
81
  * Gestore chiamato quando il popup viene mostrato.
56
82
  * Calcola e imposta le dimensioni minime del popup in base allo spazio disponibile.
@@ -59,6 +85,7 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
59
85
  const handleShown = () => {
60
86
  // Usa requestAnimationFrame per attendere il completamento del rendering
61
87
  requestAnimationFrame(() => {
88
+ applyContentFillLayout();
62
89
  if (popupRef.current && popupRef.current.instance) {
63
90
  const instance = popupRef.current.instance();
64
91
  if (instance) {
@@ -85,7 +112,17 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
85
112
  useEffect(() => {
86
113
  setInitialWidth(width);
87
114
  setInitialHeight(height);
88
- }, [width, height]);
115
+ // Al cambio di dimensioni (es. cambio step) ricentra il popup con un nuovo riferimento oggetto,
116
+ // annullando l'eventuale spostamento manuale precedente
117
+ if (recenterOnResize)
118
+ setPosition({ my: 'center', at: 'center', of: window });
119
+ }, [width, height, recenterOnResize]);
120
+ // Dimensioni, testata e schermo pieno fanno rigenerare geometria ed elementi del popup:
121
+ // il layout flex del contenuto va riapplicato dopo il ridisegno (vedi applyContentFillLayout)
122
+ useEffect(() => {
123
+ const rafId = requestAnimationFrame(() => applyContentFillLayout());
124
+ return () => cancelAnimationFrame(rafId);
125
+ }, [initialWidth, initialHeight, title, isFullScreen, showPopup]);
89
126
  const handleResizeStart = () => {
90
127
  setIsResizing(true);
91
128
  };
@@ -101,15 +138,9 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
101
138
  setShowPopup(false);
102
139
  onClose && onClose();
103
140
  };
104
- return (_jsx(_Fragment, { children: isModal ? (_jsx(Popup, { ref: popupRef, showCloseButton: showHeader ? showCloseButton : false, showTitle: showHeader, animation: undefined, minWidth: minWidth, minHeight: minHeight, maxHeight: '95%', maxWidth: '95%', dragEnabled: !isResizing && !isFullScreen, resizeEnabled: resizable && !isFullScreen, position: expandable && isFullScreen ? { my: 'center', at: 'center', of: window } : undefined, width: expandable && isFullScreen ? '95%' : initialWidth, height: expandable && isFullScreen ? '95%' : initialHeight, title: title, visible: showPopup, onShown: handleShown, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd, onHiding: onHiding, toolbarItems: showHeader && expandable ? [
105
- {
106
- widget: 'dxButton',
107
- location: 'after',
108
- options: {
141
+ return (_jsx(_Fragment, { children: isModal ? (_jsxs(Popup, { ref: popupRef, showCloseButton: showHeader ? showCloseButton : false, showTitle: showHeader, animation: undefined, minWidth: minWidth, minHeight: minHeight, maxHeight: '95%', maxWidth: '95%', dragEnabled: !isResizing && !isFullScreen, resizeEnabled: resizable && !isFullScreen, position: expandable && isFullScreen ? { my: 'center', at: 'center', of: window } : position, width: expandable && isFullScreen ? '95%' : initialWidth, height: expandable && isFullScreen ? '95%' : initialHeight, title: title, visible: showPopup, onShown: handleShown, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd, onHiding: onHiding, children: [showHeader && titleActions && (_jsx(ToolbarItem, { toolbar: 'top', location: 'after', render: () => _jsx(_Fragment, { children: titleActions }) })), showHeader && expandable && (_jsx(ToolbarItem, { toolbar: 'top', location: 'after', widget: 'dxButton', options: {
109
142
  icon: isFullScreen ? svgToString(_jsx(IconWindowMinimize, {})) : svgToString(_jsx(IconWindowMaximize, {})),
110
143
  onClick: () => setIsFullScreen(!isFullScreen)
111
- }
112
- }
113
- ] : undefined, children: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }, children: [toolbar && (_jsx(StyledModalToolbar, { children: toolbar })), _jsx("div", { style: { flex: 1, overflow: 'hidden', minHeight: 0 }, children: _jsx(TMCard, { showBorder: false, padding: false, scrollY: true, children: children }) })] }) })) : (_jsxs(StyledModal, { "$isModal": isModal, className: "temp-modal", "$fontSize": fontSize, "$width": initialWidth, "$height": initialHeight, children: [toolbar ? _jsx(StyledModalToolbar, { children: toolbar }) : _jsx(_Fragment, {}), _jsx(StyledModalContext, { children: children })] })) }));
144
+ } })), _jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', width: '100%' }, children: [toolbar && (_jsx(StyledModalToolbar, { children: toolbar })), _jsx("div", { style: { flex: 1, overflow: 'hidden', minHeight: 0 }, children: _jsx(TMCard, { showBorder: false, padding: false, scrollY: true, children: children }) })] })] })) : (_jsxs(StyledModal, { "$isModal": isModal, className: "temp-modal", "$fontSize": fontSize, "$width": initialWidth, "$height": initialHeight, children: [toolbar ? _jsx(StyledModalToolbar, { children: toolbar }) : _jsx(_Fragment, {}), _jsx(StyledModalContext, { children: children })] })) }));
114
145
  };
115
146
  export default TMModal;
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { DcmtTypeDescriptor, HomeBlogPost, LayoutModes, ObjectRef, SearchResultDescriptor, TaskDescriptor, ValidationItem } from '@topconsultnpm/sdk-ts';
3
3
  import { DcmtInfo, FormModes, MetadataValueDescriptorEx, TaskContext, IGraphometricManagerProp } from '../../../ts';
4
4
  import { IntesiCertificateData } from '../../../helper';
5
+ import { ArchiveListMode } from '../../../hooks/useArchiveListForm';
5
6
  /**
6
7
  * Definisce il contesto da cui è stato invocato il TMDcmtForm.
7
8
  * Permette di gestire logiche diverse in base alla provenienza.
@@ -77,6 +78,12 @@ interface ITMDcmtFormProps {
77
78
  mid: number;
78
79
  value: string;
79
80
  }>, tid?: number) => void;
81
+ /**
82
+ * Modalità "aggiungi a elenco" (es. archiviazione dettagli multipli): se valorizzata, in layout di archiviazione
83
+ * il form non archivia ma emette i metadati compilati al chiamante (pulsanti "Aggiungi"/"Modifica" + "Pulisci").
84
+ * Tutta la logica è incapsulata in useArchiveListForm; usata solo da TMMultiMasterDetailDcmts.
85
+ */
86
+ archiveListMode?: ArchiveListMode;
80
87
  fetchRemoteCertificates?: (email: string) => Promise<IntesiCertificateData[]>;
81
88
  datagridUtility?: {
82
89
  onRefreshSearchAsyncDatagrid?: () => Promise<void>;
@@ -5,7 +5,7 @@ import { AccessLevelsEx, AppModules, ArchiveConstraints, ArchiveEngineByID, Dcmt
5
5
  import { FormModes, SearchResultContext } from '../../../ts';
6
6
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
7
7
  import { getWorkItemSetIDAsync, handleArchiveVisibility, searchResultToMetadataValues } from '../../../helper/queryHelper';
8
- import { genUniqueId, IconShow, SDKUI_Localizator, updateMruTids, IconBoard, IconDcmtTypeSys, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, isTaskMoreInfo, IconWorkflow, IconSearch, deepCompare, IconActivity, getExceptionMessage, isApprovalWorkflowView, getDcmtCicoStatus, buildWorkItemsFromWFCtrl, IconLock, getDcmtFormToolbarVisibility, taskModalSizes } from '../../../helper';
8
+ import { genUniqueId, IconShow, SDKUI_Localizator, updateMruTids, IconBoard, IconDcmtTypeSys, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, isTaskMoreInfo, IconWorkflow, IconSearch, deepCompare, IconActivity, getExceptionMessage, isApprovalWorkflowView, getDcmtCicoStatus, buildWorkItemsFromWFCtrl, IconLock, getDcmtFormToolbarVisibility, taskModalSizes, IconArchiveMultipleDetailDocuments } from '../../../helper';
9
9
  import { hasDetailRelations, hasMasterRelations } from '../../../helper/dcmtsHelper';
10
10
  import { Gutters, TMColors } from '../../../utils/theme';
11
11
  import { StyledFormButtonsContainer, StyledLoadingContainer, StyledSpinner, StyledToolbarCardContainer } from '../../base/Styled';
@@ -40,6 +40,8 @@ import TMToppyDraggableHelpCenter from '../assistant/TMToppyDraggableHelpCenter'
40
40
  import TMTaskForm from '../tasks/TMTaskForm';
41
41
  import { useDocumentOperations } from '../../../hooks/useDocumentOperations';
42
42
  import TMDcmtFormActionButtons from './TMDcmtFormActionButtons';
43
+ import { useArchiveListForm } from '../../../hooks/useArchiveListForm';
44
+ import TMMultiMasterDetailDcmtsForm from './TMMultiMasterDetailDcmtsForm';
43
45
  //#region Interfaces, Types and Enums
44
46
  /**
45
47
  * Definisce il contesto da cui è stato invocato il TMDcmtForm.
@@ -58,7 +60,7 @@ export var InvocationContext;
58
60
  let abortControllerLocal = new AbortController();
59
61
  ;
60
62
  //#endregion
61
- const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMode = FormModes.Update, invocationContext = InvocationContext.Default, showHeader = true, showBackButton = true, showDcmtFormSidebar = true, isClosable = false, showTodoDcmtForm = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, isModal = false, titleModal, widthModal = "100%", heightModal = "100%", allowNavigation = true, canNext, canPrev, count, itemIndex, onNext, onPrev, inputFile = null, inputMids = [], connectorFileSave = undefined, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowRelations = true, allowButtonsRefs = false, openS4TViewer = false, enableDragDropOverlay = false, editPdfForm = false, onClose, onSavedAsyncCallback, onSaveRecents, onWFOperationCompleted, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, onTaskCompleted, onTaskCreateRequest, moreInfoTasks, taskFormDialogComponent, handleNavigateToWGs, handleNavigateToDossiers, onReferenceClick, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, s4TViewerDialogComponent, onScanRequest, passToSearch, fetchRemoteCertificates, datagridUtility, graphometricManager }) => {
63
+ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMode = FormModes.Update, invocationContext = InvocationContext.Default, showHeader = true, showBackButton = true, showDcmtFormSidebar = true, isClosable = false, showTodoDcmtForm = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, isModal = false, titleModal, widthModal = "100%", heightModal = "100%", allowNavigation = true, canNext, canPrev, count, itemIndex, onNext, onPrev, inputFile = null, inputMids = [], connectorFileSave = undefined, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowRelations = true, allowButtonsRefs = false, openS4TViewer = false, enableDragDropOverlay = false, editPdfForm = false, onClose, onSavedAsyncCallback, onSaveRecents, onWFOperationCompleted, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, onTaskCompleted, onTaskCreateRequest, moreInfoTasks, taskFormDialogComponent, handleNavigateToWGs, handleNavigateToDossiers, onReferenceClick, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, s4TViewerDialogComponent, onScanRequest, passToSearch, archiveListMode, fetchRemoteCertificates, datagridUtility, graphometricManager }) => {
62
64
  const { onRefreshSearchAsyncDatagrid, onRefreshBlogDatagrid, onRefreshPreviewDatagrid } = datagridUtility || {};
63
65
  const floatingBarContainerRef = useRef(null);
64
66
  const [id, setID] = useState('');
@@ -97,6 +99,10 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
97
99
  const [isOpenFormulaEditor, setIsOpenFormulaEditor] = useState(false);
98
100
  const [currentTIDHasDetailRelations, setCurrentTIDHasDetailRelations] = useState();
99
101
  const [currentTIDHasMasterRelations, setCurrentTIDHasMasterRelations] = useState();
102
+ // Modale "Archivia documenti di dettaglio multipli" aperto dall'azione secondaria del form di archiviazione:
103
+ // dopo aver archiviato il master si apre il modale con il master appena archiviato come documento di partenza.
104
+ const [showMultiDetailArchiveModal, setShowMultiDetailArchiveModal] = useState(false);
105
+ const [multiDetailMasterDcmts, setMultiDetailMasterDcmts] = useState([]);
100
106
  const [currentDcmt, setCurrentDcmt] = useState();
101
107
  const [dcmtFile, setDcmtFile] = useState(null);
102
108
  const [focusedMetadataValue, setFocusedMetadataValue] = useState();
@@ -455,7 +461,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
455
461
  openTaskFormHandler,
456
462
  },
457
463
  });
458
- const { handleSignApprove, checkoutInfo: { showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, }, dcmtOperations: { abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
464
+ const { handleSignApprove, checkoutInfo: { showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, }, dcmtOperations: { abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, canArchiveDetailRelation, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
459
465
  useEffect(() => {
460
466
  const fetchAllUsers = async () => {
461
467
  const users = await UserListCacheService.GetAllAsync();
@@ -813,10 +819,50 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
813
819
  // in consultazione, senza errori, non dentro master/detail).
814
820
  const showToppyForCaseFlow = useMemo(() => layoutMode === LayoutModes.Update && !fetchError && caseFlowWorkItems.length > 0 && !isOpenDetails && !isOpenMaster, [layoutMode, fetchError, caseFlowWorkItems.length, isOpenDetails, isOpenMaster]);
815
821
  const isToppyVisible = useMemo(() => Boolean((showToppyForApprove || showToppyForCompleteMoreInfo || showToppyForReferences || showToppyForCaseFlow) && !openS4TViewer && !showTodoDcmtForm && !editPdfForm), [showToppyForApprove, showToppyForCompleteMoreInfo, showToppyForReferences, showToppyForCaseFlow, openS4TViewer, showTodoDcmtForm, editPdfForm]);
816
- const isModified = useMemo(() => calcIsModified(formData, formDataOrig), [formData, formDataOrig]);
822
+ const handleClearForm = useCallback(() => {
823
+ let data = structuredClone(formData);
824
+ if (!data || data.length === 0)
825
+ return;
826
+ for (let md of data) {
827
+ if (!md.mid)
828
+ continue;
829
+ if (md.mid > 99) {
830
+ const defaultValue = md.md?.defaultValue;
831
+ if (defaultValue) {
832
+ if (FormulaHelper.isFormula(defaultValue)) {
833
+ md.value = undefined;
834
+ }
835
+ else {
836
+ md.value = defaultValue;
837
+ }
838
+ }
839
+ else if (formDataOrig.find(metadata => metadata.mid === md.mid)?.value === undefined) {
840
+ if (md.value)
841
+ md.value = undefined;
842
+ }
843
+ else if (md.value && md.value.length > 0) {
844
+ md.value = '';
845
+ }
846
+ }
847
+ }
848
+ setFormData(data);
849
+ }, [formData, formDataOrig]);
850
+ // Modalità "aggiungi a elenco" (archiviazione dettagli multipli): tutta la logica specifica è nell'hook.
851
+ const archiveList = useArchiveListForm({
852
+ mode: archiveListMode,
853
+ editingRowValues: inputMids,
854
+ formDataOrig,
855
+ formDataRef,
856
+ dcmtFileRef,
857
+ tid: TID,
858
+ isModal,
859
+ id,
860
+ clearForm: handleClearForm,
861
+ });
862
+ const isModified = useMemo(() => calcIsModified(formData, archiveList.referenceValues), [formData, archiveList.referenceValues]);
817
863
  const formToolbar = useMemo(() => _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [allowNavigation && canPrev != undefined && _jsx("p", { style: { textAlign: 'center', padding: '1px 4px', display: 'flex' }, children: `${itemIndex}/${count}` }), allowNavigation && canPrev != undefined && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', iconColor: 'white', isModified: isModified, formMode: formMode, canPrev: canPrev, onPrev: onPrev }), allowNavigation && canNext != undefined && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', iconColor: 'white', isModified: isModified, formMode: formMode, canNext: canNext, onNext: onNext }), layoutMode === LayoutModes.Update &&
818
- _jsx(ContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) }), layoutMode === LayoutModes.Ark &&
819
- _jsx(TMTooltip, { content: SDKUI_Localizator.PassToSearch, position: 'bottom', children: _jsx(IconSearch, { style: { cursor: 'pointer' }, onClick: handlePassToSearch }) })] }), [allowNavigation, canPrev, canNext, itemIndex, count, isModified, formMode, onPrev, onNext, layoutMode, operationItems, handlePassToSearch]);
864
+ _jsx(ContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) }), layoutMode === LayoutModes.Ark && !archiveList.enabled &&
865
+ _jsx(TMTooltip, { content: SDKUI_Localizator.PassToSearch, position: 'bottom', children: _jsx(IconSearch, { style: { cursor: 'pointer' }, onClick: handlePassToSearch }) })] }), [allowNavigation, canPrev, canNext, itemIndex, count, isModified, formMode, onPrev, onNext, layoutMode, operationItems, handlePassToSearch, archiveList.enabled]);
820
866
  const handleUndo = useCallback(() => {
821
867
  setFormData(structuredClone(formDataOrig));
822
868
  }, [formDataOrig]);
@@ -972,9 +1018,13 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
972
1018
  const isDuplication = layoutMode === LayoutModes.Ark && DID;
973
1019
  if (isDuplication)
974
1020
  onClose?.();
1021
+ // Ritorna il DID del documento appena archiviato, così il chiamante può proseguire
1022
+ // (es. aprire il modale di archiviazione dei documenti di dettaglio sul master appena creato).
1023
+ return newDID;
975
1024
  }
976
1025
  catch (err) {
977
1026
  TMExceptionBoxManager.show({ exception: err });
1027
+ return undefined;
978
1028
  }
979
1029
  finally {
980
1030
  setShowWaitPanelLocal(false);
@@ -993,34 +1043,24 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
993
1043
  },
994
1044
  });
995
1045
  }, [isModal, id, layoutMode, handleSave, handleArchiveCompleted]);
996
- const handleClearForm = useCallback(() => {
997
- let data = structuredClone(formData);
998
- if (!data || data.length === 0)
999
- return;
1000
- for (let md of data) {
1001
- if (!md.mid)
1002
- continue;
1003
- if (md.mid > 99) {
1004
- const defaultValue = md.md?.defaultValue;
1005
- if (defaultValue) {
1006
- if (FormulaHelper.isFormula(defaultValue)) {
1007
- md.value = undefined;
1008
- }
1009
- else {
1010
- md.value = defaultValue;
1011
- }
1012
- }
1013
- else if (formDataOrig.find(metadata => metadata.mid === md.mid)?.value === undefined) {
1014
- if (md.value)
1015
- md.value = undefined;
1016
- }
1017
- else if (md.value && md.value.length > 0) {
1018
- md.value = '';
1019
- }
1020
- }
1021
- }
1022
- setFormData(data);
1023
- }, [formData, formDataOrig]);
1046
+ // Azione secondaria del form di archiviazione: archivia il master e, subito dopo,
1047
+ // apre il modale di archiviazione dei documenti di dettaglio multipli usando il master appena creato.
1048
+ const handleArchiveMasterAndDetails = useCallback(async () => {
1049
+ TMMessageBoxManager.show({
1050
+ parentId: isModal ? "TMDcmtFormShowConfirmForClose-" + id : undefined,
1051
+ buttons: [ButtonNames.YES, ButtonNames.NO],
1052
+ message: SDKUI_Localizator.ArchiveMasterAndDetails_Confirm,
1053
+ onButtonClick: async (e) => {
1054
+ if (e !== ButtonNames.YES)
1055
+ return;
1056
+ const newDID = await handleArchiveCompleted();
1057
+ if (newDID === undefined || newDID === null)
1058
+ return;
1059
+ setMultiDetailMasterDcmts([{ TID: TID, DID: newDID }]);
1060
+ setShowMultiDetailArchiveModal(true);
1061
+ },
1062
+ });
1063
+ }, [isModal, id, handleArchiveCompleted, TID]);
1024
1064
  const clearFormBtnDisabled = useMemo(() => formData.filter(md => md.mid && md.value && md.mid > 99 && md.value.length > 0).length > 0, [formData]);
1025
1065
  const archiveBtnDisabled = useMemo(() => layoutMode === LayoutModes.Update || (fromDTD?.archiveConstraint === ArchiveConstraints.ContentCompulsory && !dcmtFile) || validationItems.length > 0, [layoutMode, fromDTD?.archiveConstraint, dcmtFile, validationItems.length]);
1026
1066
  const undoBtnDisabled = useMemo(() => layoutMode === LayoutModes.Update || !isModified, [layoutMode, isModified]);
@@ -1029,10 +1069,14 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1029
1069
  const metadataValuesSource = useMemo(() => {
1030
1070
  return applyMetadataFilter(formData, showAll, listMaxItems, TID);
1031
1071
  }, [showAll, formData, listMaxItems, TID]);
1032
- const metadataValuesSourceOrig = useMemo(() => {
1033
- return applyMetadataFilter(formDataOrig, showAll, listMaxItems, TID);
1034
- }, [showAll, formDataOrig, listMaxItems, TID]);
1072
+ const metadataValuesSourceOrig = useMemo(() =>
1073
+ // I valori di riferimento (in modifica riga includono i valori già caricati) li calcola l'hook:
1074
+ // così i campi pre-compilati non si colorano di giallo finché l'utente non li cambia davvero.
1075
+ applyMetadataFilter(archiveList.referenceValues, showAll, listMaxItems, TID), [showAll, archiveList.referenceValues, listMaxItems, TID]);
1035
1076
  const handleClose = useCallback(() => {
1077
+ // Modalità "aggiungi a elenco": la X non archivia ma aggiorna l'elenco (gestita dall'hook).
1078
+ if (archiveList.requestClose(isModified, onClose))
1079
+ return;
1036
1080
  if (!isModified) {
1037
1081
  onClose?.();
1038
1082
  return;
@@ -1053,7 +1097,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1053
1097
  }
1054
1098
  }
1055
1099
  });
1056
- }, [isModified, onClose, isModal, id, handleSave]);
1100
+ }, [isModified, onClose, isModal, id, handleSave, archiveList]);
1057
1101
  const getFormula = useCallback(() => {
1058
1102
  let fd = new FormulaDescriptor();
1059
1103
  fd.expression = FormulaHelper.isFormula(focusedMetadataValue?.value) ? FormulaHelper.removeFormulaTag(focusedMetadataValue?.value) : undefined;
@@ -1126,7 +1170,14 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1126
1170
  break;
1127
1171
  }
1128
1172
  } }), _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 })] }) :
1129
- _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 })] }) }) }), customButton && _jsx(TMCustomButton, { button: customButton, formData: formData, selectedItems: [], onClose: () => setCustomButton(undefined) }), totalItems > listMaxItems &&
1173
+ archiveList.enabled ?
1174
+ _jsxs(_Fragment, { children: [_jsx(TMButton, { disabled: archiveBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconBoxArchiveIn, {}), width: 'auto', showTooltip: false, caption: archiveList.isEditingRow ? SDKUI_Localizator.Update : SDKUI_Localizator.Add, advancedColor: TMColors.success, onClick: archiveList.submit }), _jsx(TMButton, { disabled: !clearFormBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconClear, {}), width: 'auto', showTooltip: false, caption: SDKUI_Localizator.Clear, advancedColor: TMColors.tertiary, onClick: handleClearForm })] }) :
1175
+ _jsxs(_Fragment, { children: [_jsx(TMButton, { disabled: archiveBtnDisabled, btnStyle: 'advanced', icon: _jsx(IconBoxArchiveIn, {}), width: canArchiveDetailRelation ? '120px' : 'auto', showTooltip: false, caption: SDKUI_Localizator.Archive, advancedColor: TMColors.success, onClick: handleConfirmAction, secondaryActions: canArchiveDetailRelation ? [{
1176
+ name: SDKUI_Localizator.ArchiveMasterAndMultipleDetailDocuments,
1177
+ icon: _jsx(IconArchiveMultipleDetailDocuments, {}),
1178
+ disabled: archiveBtnDisabled,
1179
+ onClick: handleArchiveMasterAndDetails,
1180
+ }] : undefined }), _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 })] }) }) }), customButton && _jsx(TMCustomButton, { button: customButton, formData: formData, selectedItems: [], onClose: () => setCustomButton(undefined) }), totalItems > listMaxItems &&
1130
1181
  !isApprView &&
1131
1182
  TID !== SystemTIDs.Drafts &&
1132
1183
  !shouldShowAll &&
@@ -1157,6 +1208,8 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1157
1208
  handleConfirmAction,
1158
1209
  handleUndo,
1159
1210
  handleClearForm,
1211
+ canArchiveDetailRelation,
1212
+ handleArchiveMasterAndDetails,
1160
1213
  isReadOnlyOriginCallback,
1161
1214
  metadataDcmtOrigin?.fromTID,
1162
1215
  ]);
@@ -1584,7 +1637,8 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1584
1637
  };
1585
1638
  return (_jsxs("div", { style: { width: '100%', height: '100%' }, children: [(isModal && onClose)
1586
1639
  ? _jsx(TMModal, { title: titleModal, onClose: handleClose, width: widthModal ?? '100%', height: heightModal ?? '100%', hidePopup: false, askClosingConfirm: true, children: _jsx("div", { style: { width: "100%", height: "100%", display: 'block', padding: "4px", position: 'relative' }, children: renderDcmtForm() }) })
1587
- : renderDcmtForm(), (showMoreInfoTaskPopup && showMoreInfoTaskTask && showMoreInfoTaskTask.id && handleNavigateToWGs && handleNavigateToDossiers && getAllTasks && deleteTaskByIdsCallback && addTaskCallback && editTaskCallback) && _jsx(TMTaskForm, { id: Number(showMoreInfoTaskTask.id), title: showMoreInfoTaskTask.name ?? 'N/A', formMode: FormModes.Update, onSaved: onSavedTaskFormCallback, editTaskCallback: editTaskCallback, onClose: () => { setShowMoreInfoTaskPopup(false); setShowMoreInfoTaskTask(undefined); }, onCancel: () => { setShowMoreInfoTaskPopup(false); setShowMoreInfoTaskTask(undefined); }, isModal: true, showBackButton: false, hasNavigation: false, width: taskModalSizes(deviceType, FormModes.Update).width, height: taskModalSizes(deviceType, FormModes.Update).height, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, visualizedTasks: [], currentTask: null, setCurrentTask: () => { }, selectedRowKeys: [], handleFocusedRowKeyChange: () => { }, onStatusChanged: () => { }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback })] }));
1640
+ : renderDcmtForm(), (showMoreInfoTaskPopup && showMoreInfoTaskTask && showMoreInfoTaskTask.id && handleNavigateToWGs && handleNavigateToDossiers && getAllTasks && deleteTaskByIdsCallback && addTaskCallback && editTaskCallback) && _jsx(TMTaskForm, { id: Number(showMoreInfoTaskTask.id), title: showMoreInfoTaskTask.name ?? 'N/A', formMode: FormModes.Update, onSaved: onSavedTaskFormCallback, editTaskCallback: editTaskCallback, onClose: () => { setShowMoreInfoTaskPopup(false); setShowMoreInfoTaskTask(undefined); }, onCancel: () => { setShowMoreInfoTaskPopup(false); setShowMoreInfoTaskTask(undefined); }, isModal: true, showBackButton: false, hasNavigation: false, width: taskModalSizes(deviceType, FormModes.Update).width, height: taskModalSizes(deviceType, FormModes.Update).height, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, visualizedTasks: [], currentTask: null, setCurrentTask: () => { }, selectedRowKeys: [], handleFocusedRowKeyChange: () => { }, onStatusChanged: () => { }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback }), (showMultiDetailArchiveModal && fromDTD && multiDetailMasterDcmts.length > 0) &&
1641
+ _jsx(TMMultiMasterDetailDcmtsForm, { dtd: fromDTD, masterDcmt: multiDetailMasterDcmts[0], onClose: () => { setShowMultiDetailArchiveModal(false); setMultiDetailMasterDcmts([]); }, 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, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest })] }));
1588
1642
  };
1589
1643
  export default TMDcmtForm;
1590
1644
  /**