@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.
- package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
- package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
- package/lib/components/base/TMDataGrid.d.ts +2 -0
- package/lib/components/base/TMDataGrid.js +68 -4
- package/lib/components/base/TMModal.d.ts +4 -0
- package/lib/components/base/TMModal.js +42 -11
- package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
- package/lib/components/features/documents/TMDcmtForm.js +94 -40
- package/lib/components/features/documents/TMMasterDetailDcmts.js +88 -30
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
- package/lib/components/features/documents/TMMasterInfoFields.js +172 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +256 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +352 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +221 -0
- package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
- package/lib/components/features/documents/TMRelationViewer.js +69 -20
- package/lib/components/features/search/TMSearchResult.d.ts +8 -0
- package/lib/components/features/search/TMSearchResult.js +131 -23
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
- package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
- package/lib/components/layout/panelManager/types.d.ts +3 -0
- package/lib/helper/SDKUI_Globals.d.ts +34 -0
- package/lib/helper/SDKUI_Globals.js +37 -0
- package/lib/helper/SDKUI_Localizator.d.ts +18 -0
- package/lib/helper/SDKUI_Localizator.js +180 -0
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +3 -0
- package/lib/helper/dcmtsHelper.d.ts +20 -0
- package/lib/helper/dcmtsHelper.js +58 -1
- package/lib/helper/helpers.d.ts +9 -0
- package/lib/helper/helpers.js +8 -0
- package/lib/hooks/useArchiveListForm.d.ts +39 -0
- package/lib/hooks/useArchiveListForm.js +46 -0
- package/lib/hooks/useDocumentOperations.d.ts +1 -0
- package/lib/hooks/useDocumentOperations.js +16 -4
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +97 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +555 -0
- package/lib/hooks/useRelatedDocuments.js +2 -26
- 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
|
-
|
|
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 }),
|
|
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 {
|
|
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
|
-
|
|
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 ? (
|
|
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
|
|
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
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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
|
-
|
|
1034
|
-
|
|
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
|
-
|
|
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
|
/**
|