@topconsultnpm/sdkui-react 6.22.0-dev2.37 → 6.22.0-dev2.39
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/base/TMModal.d.ts +7 -0
- package/lib/components/base/TMModal.js +7 -4
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +18 -7
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +8 -1
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +24 -0
- package/lib/components/features/search/TMSearchResult.d.ts +6 -0
- package/lib/components/features/search/TMSearchResult.js +93 -82
- package/lib/devextreme-license.d.ts +1 -1
- package/lib/devextreme-license.js +1 -1
- package/lib/helper/SDKUI_Globals.d.ts +22 -3
- package/lib/helper/SDKUI_Globals.js +19 -5
- package/lib/helper/SDKUI_Localizator.d.ts +9 -0
- package/lib/helper/SDKUI_Localizator.js +90 -0
- package/lib/hooks/useDocumentOperations.d.ts +2 -0
- package/lib/hooks/useDocumentOperations.js +9 -2
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +5 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +145 -18
- package/package.json +1 -1
|
@@ -18,6 +18,13 @@ interface ITMModal {
|
|
|
18
18
|
recenterOnResize?: boolean;
|
|
19
19
|
/** Contenuto aggiuntivo nella barra del titolo, prima del pulsante di chiusura (es. un menu di opzioni) */
|
|
20
20
|
titleActions?: React.ReactNode;
|
|
21
|
+
/** Dimensioni in px al termine di un ridimensionamento manuale */
|
|
22
|
+
onResized?: (size: {
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
}) => void;
|
|
26
|
+
/** A ogni cambio di valore le dimensioni tornano a `width`/`height`, anche se invariate */
|
|
27
|
+
sizeResetToken?: number | string;
|
|
21
28
|
}
|
|
22
29
|
declare const TMModal: React.FC<ITMModal>;
|
|
23
30
|
export default TMModal;
|
|
@@ -42,7 +42,7 @@ const StyledModalContext = styled.div `
|
|
|
42
42
|
overflow: auto;
|
|
43
43
|
height: 100%;
|
|
44
44
|
`;
|
|
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 }) => {
|
|
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, onResized, sizeResetToken }) => {
|
|
46
46
|
const popupRef = useRef(null);
|
|
47
47
|
const [initialWidth, setInitialWidth] = useState(width);
|
|
48
48
|
const [initialHeight, setInitialHeight] = useState(height);
|
|
@@ -116,7 +116,8 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
116
116
|
// annullando l'eventuale spostamento manuale precedente
|
|
117
117
|
if (recenterOnResize)
|
|
118
118
|
setPosition({ my: 'center', at: 'center', of: window });
|
|
119
|
-
|
|
119
|
+
// Il token annulla un ridimensionamento manuale, che non cambia width/height
|
|
120
|
+
}, [width, height, recenterOnResize, sizeResetToken]);
|
|
120
121
|
// Dimensioni, testata e schermo pieno fanno rigenerare geometria ed elementi del popup:
|
|
121
122
|
// il layout flex del contenuto va riapplicato dopo il ridisegno (vedi applyContentFillLayout)
|
|
122
123
|
useEffect(() => {
|
|
@@ -128,8 +129,10 @@ const TMModal = ({ resizable = true, expandable = false, isModal = true, title =
|
|
|
128
129
|
};
|
|
129
130
|
const handleResizeEnd = (e) => {
|
|
130
131
|
setIsResizing(false);
|
|
131
|
-
|
|
132
|
-
|
|
132
|
+
// Le dimensioni arrivano in px: senza unità non sarebbero un valore CSS valido
|
|
133
|
+
setInitialWidth(`${e.width}px`);
|
|
134
|
+
setInitialHeight(`${e.height}px`);
|
|
135
|
+
onResized?.({ width: e.width, height: e.height });
|
|
133
136
|
};
|
|
134
137
|
const onHiding = (e) => {
|
|
135
138
|
if (askClosingConfirm)
|
|
@@ -3,7 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
3
3
|
import { LayoutModes } from "@topconsultnpm/sdk-ts";
|
|
4
4
|
import { LoadIndicator } from "devextreme-react/load-indicator";
|
|
5
5
|
import { SearchResultContext } from "../../../ts";
|
|
6
|
-
import { getFullFileExtension, getMultiDetailPanelStates, hasMultiDetailPanelStates, hoverStyle, IconArchive, IconDelete, IconDuplicate, IconEdit, IconEraser, IconInfo, IconMenuVertical, IconRefresh, IconSearch, IconSearchCheck, saveMultiDetailPanelStates, SDKUI_Localizator } from "../../../helper";
|
|
6
|
+
import { getFullFileExtension, getMultiDetailModalSize, getMultiDetailPanelStates, hasMultiDetailPanelStates, hoverStyle, IconArchive, IconDelete, IconDuplicate, IconEdit, IconEraser, IconInfo, IconMenuVertical, IconRefresh, IconSearch, IconSearchCheck, saveMultiDetailModalSize, saveMultiDetailPanelStates, SDKUI_Localizator } from "../../../helper";
|
|
7
7
|
import { TMColors } from "../../../utils/theme";
|
|
8
8
|
import { DeviceType, useDeviceType } from "../../base/TMDeviceProvider";
|
|
9
9
|
import { TMLayoutWaitingContainer } from "../../base/TMWaitPanel";
|
|
@@ -46,7 +46,7 @@ const styles = {
|
|
|
46
46
|
const MOBILE_SECTION = 'archiveList';
|
|
47
47
|
const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
48
48
|
const { dtd, masterDcmt, onClose, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showDcmtFormSidebar, showTodoDcmtForm, openFileUploaderPdfEditor, graphometricManager, editPdfForm, openS4TViewer, showToppyDraggableHelpCenter, toppyHelpCenterUsePortal, fetchRemoteCertificates, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onReferenceClick, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, onFileOpened, passToArchiveCallback, openWGsCopyMoveForm } = props;
|
|
49
|
-
const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, hasSavedLayout, setHasSavedLayout, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
|
|
49
|
+
const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, hasSavedLayout, setHasSavedLayout, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, copyArchivedDetailToRows, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
|
|
50
50
|
const deviceType = useDeviceType();
|
|
51
51
|
// Su mobile il panel manager mostra un pannello alla volta: le sezioni si raggiungono dalla sua barra
|
|
52
52
|
const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
|
|
@@ -75,6 +75,15 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
75
75
|
onClick: () => setShowGridSearch(prev => !prev),
|
|
76
76
|
};
|
|
77
77
|
const isSummaryPhase = phase === MultiDetailArchivePhase.ArchiveSummary;
|
|
78
|
+
// Dimensioni del modale, ricordate per passo. Si salvano senza aggiornare lo stato: il cambio di
|
|
79
|
+
// dimensioni ricentrerebbe il popup appena ridimensionato dall'utente
|
|
80
|
+
const modalStep = isSummaryPhase ? 'archiveSummary' : 'selectDcmtType';
|
|
81
|
+
const defaultModalSize = isSummaryPhase ? { width: '95%', height: '95%' } : { width: '720px', height: 'auto' };
|
|
82
|
+
const modalSize = useMemo(() => getMultiDetailModalSize(modalStep) ?? defaultModalSize, [modalStep, layoutResetToken]);
|
|
83
|
+
const persistModalSize = useCallback((size) => {
|
|
84
|
+
saveMultiDetailModalSize(modalStep, { width: `${size.width}px`, height: `${size.height}px` });
|
|
85
|
+
setHasSavedLayout(true);
|
|
86
|
+
}, [modalStep, setHasSavedLayout]);
|
|
78
87
|
const baseTitle = SDKUI_Localizator.ArchiveMultipleDetailDocuments;
|
|
79
88
|
// Con un solo tipo il nome è mostrato direttamente nel titolo del modale
|
|
80
89
|
const modalTitle = !hasMultipleTypes && selectedItem ? `${baseTitle}: ${getDtdDisplayName(selectedItem.detailDtd)}` : baseTitle;
|
|
@@ -114,7 +123,7 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
114
123
|
allowSorting: false, allowFiltering: false, allowHeaderFiltering: false, allowResizing: false,
|
|
115
124
|
allowReordering: false, allowHiding: false, showInColumnChooser: false, allowEditing: false,
|
|
116
125
|
cssClass: 'tm-row-actions',
|
|
117
|
-
cellRender: (cell) => (_jsxs("div", { style: styles.actionsCell, children: [_jsx(TMTooltip, { content: SDKUI_Localizator.Update, children: _jsx("span", { style: styles.actionIcon, onClick: () => openEditForm(cell.data), children: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Duplicate, children: _jsx("span", { style: styles.actionIcon, onClick: () => duplicateRow(cell.data), children: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.
|
|
126
|
+
cellRender: (cell) => (_jsxs("div", { style: styles.actionsCell, children: [_jsx(TMTooltip, { content: SDKUI_Localizator.Update, children: _jsx("span", { style: styles.actionIcon, onClick: () => openEditForm(cell.data), children: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Duplicate, children: _jsx("span", { style: styles.actionIcon, onClick: () => duplicateRow(cell.data), children: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Remove, children: _jsx("span", { style: styles.actionIcon, onClick: () => deleteRow(cell.data), children: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }) }) })] })),
|
|
118
127
|
};
|
|
119
128
|
return [actionsColumn, iconColumn, ...detailColumns, ...(detailSupportsFile ? [fileColumn] : [])];
|
|
120
129
|
}, [detailColumns, detailSupportsFile]);
|
|
@@ -151,13 +160,13 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
151
160
|
const contextMenuItems = useMemo(() => [
|
|
152
161
|
{ name: SDKUI_Localizator.Update, icon: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }), operationType: 'singleRow', onClick: (id) => openEditForm(archiveRows.find(r => r.id === id)) },
|
|
153
162
|
{ name: SDKUI_Localizator.Duplicate, icon: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }), operationType: 'singleRow', onClick: (id) => duplicateRow(archiveRows.find(r => r.id === id)) },
|
|
154
|
-
{ name: SDKUI_Localizator.
|
|
163
|
+
{ name: SDKUI_Localizator.Remove, icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }), operationType: 'singleRow', beginGroup: true, onClick: (id) => deleteRow(archiveRows.find(r => r.id === id)) },
|
|
155
164
|
toggleGridSearchItem,
|
|
156
165
|
], [archiveRows, showGridSearch]);
|
|
157
166
|
// Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo.
|
|
158
167
|
// Con il layout di default non c'è niente da cancellare, quindi la voce non compare
|
|
159
168
|
const optionsMenuItems = useMemo(() => hasSavedLayout
|
|
160
|
-
? [{ name:
|
|
169
|
+
? [{ name: SDKUI_Localizator.RemoveLayout, icon: _jsx(IconEraser, { fontSize: 18, color: TMColors.error }), onClick: clearLayout }]
|
|
161
170
|
: [], [clearLayout, hasSavedLayout]);
|
|
162
171
|
// Le opzioni riguardano i pannelli del riepilogo: fuori da quella fase non hanno effetto, e su mobile
|
|
163
172
|
// non c'è un layout da cancellare (i pannelli si vedono uno alla volta, a tutta altezza)
|
|
@@ -202,7 +211,7 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
202
211
|
}), isOpen && (_jsx("div", { style: { ...accordionStyles.accordionListBody, ...(panel.isMaximized ? { borderTop: 'none' } : {}) }, children: isLoadingArchivedDetails
|
|
203
212
|
? _jsxs("div", { style: accordionStyles.accordionListPlaceholder, children: [_jsx(LoadIndicator, { height: 30, width: 30 }), SDKUI_Localizator.Loading] })
|
|
204
213
|
: (hasResults
|
|
205
|
-
? _jsx(TMSearchResult, { showToolbarHeader: false, showCounterConfig: false, showActionsColumn: true, compactPager: !panel.isMaximized, context: SearchResultContext.METADATA_SEARCH, searchResults: archivedDetailResults, onClose: () => setSectionOpen('archivedDetails', false), showBackButton: false, showToolbarCloseButton: false, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, showSearchResultSidebar: false, showDcmtFormSidebar: true, autoFocusFirstRow: false, onRefreshSearchAsyncDatagrid: refreshArchivedDetailsAsync, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm })
|
|
214
|
+
? _jsx(TMSearchResult, { showToolbarHeader: false, showCounterConfig: false, showActionsColumn: true, compactPager: !panel.isMaximized, context: SearchResultContext.METADATA_SEARCH, searchResults: archivedDetailResults, onClose: () => setSectionOpen('archivedDetails', false), showBackButton: false, showToolbarCloseButton: false, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, showSearchResultSidebar: false, showDcmtFormSidebar: true, autoFocusFirstRow: false, onRefreshSearchAsyncDatagrid: refreshArchivedDetailsAsync, onRowDuplicateRequest: copyArchivedDetailToRows, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm })
|
|
206
215
|
: _jsx("div", { style: accordionStyles.accordionListPlaceholder, children: SDKUI_Localizator.NoDataToDisplay })) }))] }));
|
|
207
216
|
};
|
|
208
217
|
// ----- Sezione: dettagli da archiviare ------------------------------------------------------
|
|
@@ -276,7 +285,9 @@ const TMMultiMasterDetailDcmtsForm = (props) => {
|
|
|
276
285
|
// ----- Render principale --------------------------------------------------------------------
|
|
277
286
|
if (isLoading)
|
|
278
287
|
return null;
|
|
279
|
-
return _jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { direction: 'vertical', ...waitPanel, isCancelable: true, abortController: abortController, usePortal: true, children: _jsx(TMModal, { width:
|
|
288
|
+
return _jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { direction: 'vertical', ...waitPanel, isCancelable: true, abortController: abortController, usePortal: true, children: _jsx(TMModal, { width: modalSize.width, height: modalSize.height, onClose: closeModal, onResized: persistModalSize,
|
|
289
|
+
// Cancellato il layout il popup torna alle dimensioni di default
|
|
290
|
+
sizeResetToken: layoutResetToken, title: modalTitle, hidePopup: false, askClosingConfirm: archiveRows.length > 0, recenterOnResize: true, titleActions: renderOptionsMenu(), children: _jsxs("div", { style: styles.content, children: [hasMultipleTypes && _jsx("div", { style: styles.header, children: renderStepper({
|
|
280
291
|
steps,
|
|
281
292
|
phase,
|
|
282
293
|
hasMultipleTypes,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { DataListViewModes, DcmtTypeDescriptor, MetadataDescriptor, RelationDescriptor } from '@topconsultnpm/sdk-ts';
|
|
2
|
+
import { DataListViewModes, DcmtTypeDescriptor, MetadataDataTypes, MetadataDescriptor, RelationDescriptor } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
import { DataGridTypes, IColumnProps } from 'devextreme-react/data-grid';
|
|
4
4
|
import { TMPanelDimensionsMap } from '../../layout/panelManager/types';
|
|
5
5
|
import { DcmtInfo, MetadataValueDescriptorEx } from '../../../ts';
|
|
@@ -80,6 +80,13 @@ export declare const isInlineEditableMetadata: (md: MetadataDescriptor) => boole
|
|
|
80
80
|
* la riga porta in `__values` per l'archiviazione. Vuoto o non numerico: cella vuota
|
|
81
81
|
*/
|
|
82
82
|
export declare const toGridNumber: (value: unknown) => number | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Valore di un metadato letto da un documento archiviato, nel formato in cui il form della riga
|
|
85
|
+
* produce i suoi valori: stringa vuota se assente, data serializzata come la restituisce l'editor
|
|
86
|
+
* ('yyyy-MM-ddTHH:mm:ss', vedi TMDateBox), numero con il punto decimale. Solo così la riga copiata
|
|
87
|
+
* si rilegge nel form, si mostra in griglia e si archivia come una riga compilata a mano
|
|
88
|
+
*/
|
|
89
|
+
export declare const toArchiveRowValue: (value: MetadataScalarValue, dataType: MetadataDataTypes | undefined) => string;
|
|
83
90
|
/** Colonne della griglia di riepilogo dai metadati (utente) del tipo documento */
|
|
84
91
|
export declare const buildColumnsFromDtd: (dtd: DcmtTypeDescriptor | undefined, renderDataListCell: (value: string | Date | number | undefined, dataListID: number, viewMode: DataListViewModes) => React.ReactElement) => Array<IColumnProps>;
|
|
85
92
|
export declare const getDetailDtdAsync: (detailTID: number | undefined) => Promise<DcmtTypeDescriptor | undefined>;
|
|
@@ -47,6 +47,30 @@ export const toGridNumber = (value) => {
|
|
|
47
47
|
const parsed = typeof value === 'number' ? value : Number(value);
|
|
48
48
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
49
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* Valore di un metadato letto da un documento archiviato, nel formato in cui il form della riga
|
|
52
|
+
* produce i suoi valori: stringa vuota se assente, data serializzata come la restituisce l'editor
|
|
53
|
+
* ('yyyy-MM-ddTHH:mm:ss', vedi TMDateBox), numero con il punto decimale. Solo così la riga copiata
|
|
54
|
+
* si rilegge nel form, si mostra in griglia e si archivia come una riga compilata a mano
|
|
55
|
+
*/
|
|
56
|
+
export const toArchiveRowValue = (value, dataType) => {
|
|
57
|
+
if (value === undefined || value === null || value === '')
|
|
58
|
+
return '';
|
|
59
|
+
if (dataType === MetadataDataTypes.DateTime) {
|
|
60
|
+
const date = value instanceof Date ? value : new Date(String(value));
|
|
61
|
+
// Data non interpretabile: meglio la cella vuota di un valore che il form non saprebbe rileggere
|
|
62
|
+
if (Number.isNaN(date.getTime()))
|
|
63
|
+
return '';
|
|
64
|
+
const pad = (part) => String(part).padStart(2, '0');
|
|
65
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
66
|
+
}
|
|
67
|
+
if (dataType === MetadataDataTypes.Number) {
|
|
68
|
+
// L'archiviazione fa parseFloat del valore: serve il punto come separatore decimale
|
|
69
|
+
const parsed = typeof value === 'number' ? value : Number(String(value).trim());
|
|
70
|
+
return Number.isFinite(parsed) ? String(parsed) : '';
|
|
71
|
+
}
|
|
72
|
+
return String(value);
|
|
73
|
+
};
|
|
50
74
|
/** Colonne della griglia di riepilogo dai metadati (utente) del tipo documento */
|
|
51
75
|
export const buildColumnsFromDtd = (dtd, renderDataListCell) => getUserMetadataSorted(dtd).map(md => {
|
|
52
76
|
const dataListID = md.dataListID ?? 0;
|
|
@@ -40,6 +40,12 @@ interface ITMSearchResultProps {
|
|
|
40
40
|
showActionsColumn?: boolean;
|
|
41
41
|
/** Rende compatto il paginatore della griglia: utile quando il risultato è dentro pannelli bassi (es. accordion) */
|
|
42
42
|
compactPager?: boolean;
|
|
43
|
+
/** Sostituisce l'azione "duplica" della colonna di riga, che passa qui la riga cliccata: il menu contestuale resta invariato. I valori di riga della griglia sono stringhe */
|
|
44
|
+
onRowDuplicateRequest?: (rowData: {
|
|
45
|
+
TID?: number | string;
|
|
46
|
+
DID?: number | string;
|
|
47
|
+
FILEEXT?: string;
|
|
48
|
+
}) => void;
|
|
43
49
|
counterConfig?: ITMCounterContainerProps;
|
|
44
50
|
onClose?: () => void;
|
|
45
51
|
openInOffice?: (selectedDcmtsOrFocused: Array<DcmtInfo>) => Promise<void>;
|
|
@@ -66,7 +66,7 @@ const TMSearchResult = ({
|
|
|
66
66
|
// Data
|
|
67
67
|
groupId, searchResults = [], context = SearchResultContext.METADATA_SEARCH, title, selectedSearchResultTID, floatingActionConfig, workingGroupContext = undefined, inputDID,
|
|
68
68
|
// Boolean flags to enable/disable features
|
|
69
|
-
autoFocusFirstRow = true, formAutoOpen, isVisible = true, allowRelations = true, openDcmtFormAsModal = false, showSearchResultSidebar = true, showDcmtFormSidebar = true, showSelector = false, isClosable = false, allowFloatingBar = true, showToolbarHeader = true, showBackButton = true, showToolbarCloseButton = false, disableAccordionIfSingleCategory = false, editPdfForm = false, openS4TViewer = false, showTodoDcmtForm = false, showToppyDraggableHelpCenter = true, toppyHelpCenterUsePortal = false, showNoDcmtFoundMessage = true, enablePinIcons = true, showCounterConfig = true, showActionsColumn = false, compactPager = false,
|
|
69
|
+
autoFocusFirstRow = true, formAutoOpen, isVisible = true, allowRelations = true, openDcmtFormAsModal = false, showSearchResultSidebar = true, showDcmtFormSidebar = true, showSelector = false, isClosable = false, allowFloatingBar = true, showToolbarHeader = true, showBackButton = true, showToolbarCloseButton = false, disableAccordionIfSingleCategory = false, editPdfForm = false, openS4TViewer = false, showTodoDcmtForm = false, showToppyDraggableHelpCenter = true, toppyHelpCenterUsePortal = false, showNoDcmtFoundMessage = true, enablePinIcons = true, showCounterConfig = true, showActionsColumn = false, compactPager = false, onRowDuplicateRequest,
|
|
70
70
|
// Counters
|
|
71
71
|
counterConfig,
|
|
72
72
|
// Callbacks (optional)
|
|
@@ -396,7 +396,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
396
396
|
openTaskFormHandler,
|
|
397
397
|
},
|
|
398
398
|
});
|
|
399
|
-
const { isOpenDcmtForm, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid, showDcmtTypesSelection, showExportForm, updateBatchUpdateForm, closeDcmtFormHandler, handleSignApprove, checkoutInfo: { showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, }, dcmtOperations: { abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
|
|
399
|
+
const { isOpenDcmtForm, isDcmtFormModal, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid, showDcmtTypesSelection, showExportForm, updateBatchUpdateForm, closeDcmtFormHandler, handleSignApprove, checkoutInfo: { showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, }, dcmtOperations: { abortController, showWaitPanel, showPrimary, waitPanelTitle, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync, }, relatedDocumentsInfo: { isOpenDetails, isOpenMaster, checkRelatedDcmtsArchiveCapability, checkManyToManyCapability, }, toppyOperations: { showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, updateShowApprovePopup, updateShowRejectPopup, updateShowReAssignPopup, updateShowMoreInfoPopup } } = features;
|
|
400
400
|
// ----- Azioni della colonna di riga (showActionsColumn) -----------------------------------------
|
|
401
401
|
// Le voci di operationItems agiscono sui documenti selezionati o, in mancanza, su quello a fuoco:
|
|
402
402
|
// non accettano una riga come argomento. Il click sull'icona quindi azzera la selezione e porta il
|
|
@@ -404,6 +404,10 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
404
404
|
// l'hook ricalcola in un effect (un render più tardi): lanciare l'operazione prima significherebbe
|
|
405
405
|
// eseguirla sulla riga precedente.
|
|
406
406
|
const pendingRowActionRef = useRef(undefined);
|
|
407
|
+
// La callback è letta da una ref: dipendere dal prop renderebbe nuova requestRowAction, e con essa
|
|
408
|
+
// la colonna delle azioni, a ogni render del padre (vedi actionsColumn in TMSearchResultGrid)
|
|
409
|
+
const onRowDuplicateRequestRef = useRef(onRowDuplicateRequest);
|
|
410
|
+
onRowDuplicateRequestRef.current = onRowDuplicateRequest;
|
|
407
411
|
const requestRowAction = useCallback((operationId, rowData) => {
|
|
408
412
|
if (!showActionsColumn)
|
|
409
413
|
return;
|
|
@@ -411,6 +415,11 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
411
415
|
ShowAlert({ message: SDKUI_Localizator.InvalidDcmt, mode: "warning", duration: 3000 });
|
|
412
416
|
return;
|
|
413
417
|
}
|
|
418
|
+
// Duplica ridefinita dal chiamante: agisce sulla riga cliccata, senza toccare fuoco e selezione
|
|
419
|
+
if (operationId === 'dup' && onRowDuplicateRequestRef.current) {
|
|
420
|
+
onRowDuplicateRequestRef.current(rowData);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
414
423
|
pendingRowActionRef.current = { operationId, TID: rowData.TID, DID: rowData.DID };
|
|
415
424
|
isFirstAutoFocusRef.current = false;
|
|
416
425
|
setSelectedItems([]);
|
|
@@ -969,7 +978,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
969
978
|
], [tmSearchResult, tmBlog, tmSysMetadata, tmDcmtPreview, tmFullTextSearch, showToolbarHeader, context, isMobile, backHandler, backHandlerSecondary, isClosable, onBack]);
|
|
970
979
|
/** Contenuto del panel manager: con groupId sta nel provider del contenitore, altrimenti in quello qui sotto */
|
|
971
980
|
const panelManagerContent = (_jsxs(_Fragment, { children: [_jsx(PanelDisabledStateHandler, { isBoardDisabled: isBoardDisabled, savedBlogVisible: savedBlogVisible }), isLayoutPersisted && _jsx(SavedLayoutSyncHandler, { disabledPanelIds: disabledLayoutPanelIds }), _jsx(ShowMainPanelBridge, { showMainPanelRef: showMainPanelRef }), _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", parentId: groupId, showToolbar: showSearchResultSidebar })] }));
|
|
972
|
-
return (_jsxs(StyledMultiViewPanel, { "$isVisible": isVisible, children: [_jsx(StyledMultiViewPanel, { "$isVisible": !isOpenDcmtForm && !isOpenDetails && !isOpenMaster, style: {
|
|
981
|
+
return (_jsxs(StyledMultiViewPanel, { "$isVisible": isVisible, children: [_jsx(StyledMultiViewPanel, { "$isVisible": (!isOpenDcmtForm || isDcmtFormModal) && !isOpenDetails && !isOpenMaster, style: {
|
|
973
982
|
display: 'flex',
|
|
974
983
|
flexDirection: isMobile ? 'column' : 'row',
|
|
975
984
|
justifyContent: 'space-between',
|
|
@@ -1002,7 +1011,7 @@ const findOperationItemById = (items, id) => {
|
|
|
1002
1011
|
const rowActions = [
|
|
1003
1012
|
{ operationId: 'open-form', icon: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) },
|
|
1004
1013
|
{ operationId: 'dup', icon: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) },
|
|
1005
|
-
{ operationId: 'del-log', icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }) },
|
|
1014
|
+
{ operationId: 'del-log', icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }), isVisible: (rowData) => rowData?.ISLOGDEL != 1 },
|
|
1006
1015
|
];
|
|
1007
1016
|
const rowActionsStyles = {
|
|
1008
1017
|
cell: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' },
|
|
@@ -1449,6 +1458,8 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1449
1458
|
const operation = findOperationItemById(operationItemsRef.current, action.operationId);
|
|
1450
1459
|
if (!operation || operation.visible === false)
|
|
1451
1460
|
return null;
|
|
1461
|
+
if (action.isVisible && !action.isVisible(cellData.data))
|
|
1462
|
+
return null;
|
|
1452
1463
|
return (_jsx("span", { style: rowActionsStyles.icon, title: operation.name, onClick: () => onRowActionRequest?.(action.operationId, cellData.data), children: action.icon }, action.operationId));
|
|
1453
1464
|
}) })),
|
|
1454
1465
|
}), [onRowActionRequest]);
|
|
@@ -1499,9 +1510,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1499
1510
|
return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [!isDataGridReady && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', gap: '10px' }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })), isDataGridReady && _jsx(TMDataGrid, { ref: dataGridRef, id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, customizeColumns: customizeColumns, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: disableAutoFocus ? undefined : Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearchTMDatagrid, showFilterPanel: true, sorting: { mode: "multiple" }, selection: { mode: allowMultipleSelection ? 'multiple' : 'single' }, pageSize: pageSize, compactPager: compactPager, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: operationItems, counterConfig: gridCounterConfig })] });
|
|
1500
1511
|
};
|
|
1501
1512
|
//#region TMSearchResultSelector
|
|
1502
|
-
const StyledItemTemplate = styled.div `
|
|
1503
|
-
background: ${(props) => props.$isSelected ? 'oklch(from var(--dx-color-primary) l c h / .2) !important' : 'transparent'};
|
|
1504
|
-
cursor: pointer;
|
|
1513
|
+
const StyledItemTemplate = styled.div `
|
|
1514
|
+
background: ${(props) => props.$isSelected ? 'oklch(from var(--dx-color-primary) l c h / .2) !important' : 'transparent'};
|
|
1515
|
+
cursor: pointer;
|
|
1505
1516
|
`;
|
|
1506
1517
|
const MemoizedStyledItemTemplate = React.memo(StyledItemTemplate);
|
|
1507
1518
|
const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCategory = false, selectedTID, selectedSearchResult, autoSelectFirst = true, onSelectionChanged }) => {
|
|
@@ -1708,85 +1719,85 @@ const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, i
|
|
|
1708
1719
|
return (_jsx(TMDcmtPreview, { dcmtData: currentDcmt, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, onBack: onBack, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, isResizingActive: isResizingActive, isVisible: isVisible && isPageVisible }, refreshPreviewTrigger));
|
|
1709
1720
|
};
|
|
1710
1721
|
// Styled Components
|
|
1711
|
-
const StyledPlaceholder = styled.div `
|
|
1712
|
-
padding: 20px;
|
|
1713
|
-
text-align: center;
|
|
1714
|
-
color: #888;
|
|
1722
|
+
const StyledPlaceholder = styled.div `
|
|
1723
|
+
padding: 20px;
|
|
1724
|
+
text-align: center;
|
|
1725
|
+
color: #888;
|
|
1715
1726
|
`;
|
|
1716
|
-
const StyledIndexingInfoSection = styled.div `
|
|
1717
|
-
padding: 15px;
|
|
1718
|
-
border-top: 1px solid #e0e0e0;
|
|
1719
|
-
background: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
|
|
1720
|
-
display: flex;
|
|
1721
|
-
flex-direction: column;
|
|
1722
|
-
gap: 10px;
|
|
1727
|
+
const StyledIndexingInfoSection = styled.div `
|
|
1728
|
+
padding: 15px;
|
|
1729
|
+
border-top: 1px solid #e0e0e0;
|
|
1730
|
+
background: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
|
|
1731
|
+
display: flex;
|
|
1732
|
+
flex-direction: column;
|
|
1733
|
+
gap: 10px;
|
|
1723
1734
|
`;
|
|
1724
|
-
const StyledIndexingToggle = styled.button `
|
|
1725
|
-
display: flex;
|
|
1726
|
-
align-items: center;
|
|
1727
|
-
justify-content: space-between;
|
|
1728
|
-
width: 100%;
|
|
1729
|
-
padding: 10px 16px;
|
|
1730
|
-
background: white;
|
|
1731
|
-
border: 1px solid #d0d0d0;
|
|
1732
|
-
border-radius: 6px;
|
|
1733
|
-
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
|
1734
|
-
transition: all 0.2s ease;
|
|
1735
|
-
font-size: 14px;
|
|
1736
|
-
font-weight: 500;
|
|
1737
|
-
color: #333;
|
|
1738
|
-
opacity: ${props => props.disabled ? 0.6 : 1};
|
|
1739
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
1740
|
-
|
|
1741
|
-
&:hover:not(:disabled) {
|
|
1742
|
-
background: #f8f8f8;
|
|
1743
|
-
border-color: #2196F3;
|
|
1744
|
-
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
&:active:not(:disabled) {
|
|
1748
|
-
transform: translateY(1px);
|
|
1749
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
1750
|
-
}
|
|
1735
|
+
const StyledIndexingToggle = styled.button `
|
|
1736
|
+
display: flex;
|
|
1737
|
+
align-items: center;
|
|
1738
|
+
justify-content: space-between;
|
|
1739
|
+
width: 100%;
|
|
1740
|
+
padding: 10px 16px;
|
|
1741
|
+
background: white;
|
|
1742
|
+
border: 1px solid #d0d0d0;
|
|
1743
|
+
border-radius: 6px;
|
|
1744
|
+
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
|
1745
|
+
transition: all 0.2s ease;
|
|
1746
|
+
font-size: 14px;
|
|
1747
|
+
font-weight: 500;
|
|
1748
|
+
color: #333;
|
|
1749
|
+
opacity: ${props => props.disabled ? 0.6 : 1};
|
|
1750
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
1751
|
+
|
|
1752
|
+
&:hover:not(:disabled) {
|
|
1753
|
+
background: #f8f8f8;
|
|
1754
|
+
border-color: #2196F3;
|
|
1755
|
+
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
&:active:not(:disabled) {
|
|
1759
|
+
transform: translateY(1px);
|
|
1760
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
1761
|
+
}
|
|
1751
1762
|
`;
|
|
1752
|
-
const StyledLeftContent = styled.div `
|
|
1753
|
-
display: flex;
|
|
1754
|
-
align-items: center;
|
|
1755
|
-
gap: 8px;
|
|
1763
|
+
const StyledLeftContent = styled.div `
|
|
1764
|
+
display: flex;
|
|
1765
|
+
align-items: center;
|
|
1766
|
+
gap: 8px;
|
|
1756
1767
|
`;
|
|
1757
|
-
const StyledRightContent = styled.div `
|
|
1758
|
-
display: flex;
|
|
1759
|
-
align-items: center;
|
|
1760
|
-
gap: 8px;
|
|
1768
|
+
const StyledRightContent = styled.div `
|
|
1769
|
+
display: flex;
|
|
1770
|
+
align-items: center;
|
|
1771
|
+
gap: 8px;
|
|
1761
1772
|
`;
|
|
1762
|
-
const StyledChevron = styled.span `
|
|
1763
|
-
transition: transform 0.2s ease;
|
|
1764
|
-
transform: ${props => props.$isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
|
|
1765
|
-
color: #666;
|
|
1766
|
-
font-size: 12px;
|
|
1773
|
+
const StyledChevron = styled.span `
|
|
1774
|
+
transition: transform 0.2s ease;
|
|
1775
|
+
transform: ${props => props.$isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
|
|
1776
|
+
color: #666;
|
|
1777
|
+
font-size: 12px;
|
|
1767
1778
|
`;
|
|
1768
|
-
const StyledIndexingInfoBox = styled.div `
|
|
1769
|
-
position: relative;
|
|
1770
|
-
background: white;
|
|
1771
|
-
border: 1px solid #e0e0e0;
|
|
1772
|
-
border-radius: 6px;
|
|
1773
|
-
padding: 12px;
|
|
1774
|
-
max-height: 200px;
|
|
1775
|
-
overflow: auto;
|
|
1776
|
-
font-family: 'Courier New', monospace;
|
|
1777
|
-
font-size: 12px;
|
|
1778
|
-
color: #333;
|
|
1779
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1780
|
-
animation: slideDown 0.2s ease;
|
|
1781
|
-
|
|
1782
|
-
@keyframes slideDown {
|
|
1783
|
-
from {
|
|
1784
|
-
opacity: 0;
|
|
1785
|
-
transform: translateY(-10px);
|
|
1786
|
-
}
|
|
1787
|
-
to {
|
|
1788
|
-
opacity: 1;
|
|
1789
|
-
transform: translateY(0);
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1779
|
+
const StyledIndexingInfoBox = styled.div `
|
|
1780
|
+
position: relative;
|
|
1781
|
+
background: white;
|
|
1782
|
+
border: 1px solid #e0e0e0;
|
|
1783
|
+
border-radius: 6px;
|
|
1784
|
+
padding: 12px;
|
|
1785
|
+
max-height: 200px;
|
|
1786
|
+
overflow: auto;
|
|
1787
|
+
font-family: 'Courier New', monospace;
|
|
1788
|
+
font-size: 12px;
|
|
1789
|
+
color: #333;
|
|
1790
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1791
|
+
animation: slideDown 0.2s ease;
|
|
1792
|
+
|
|
1793
|
+
@keyframes slideDown {
|
|
1794
|
+
from {
|
|
1795
|
+
opacity: 0;
|
|
1796
|
+
transform: translateY(-10px);
|
|
1797
|
+
}
|
|
1798
|
+
to {
|
|
1799
|
+
opacity: 1;
|
|
1800
|
+
transform: translateY(0);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1792
1803
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const licenseKey = "LCPv1(
|
|
1
|
+
export declare const licenseKey = "LCPv1>7eY(j4LeYSi9_V6!J4He7,1:JfoSYV:%QDL7+aJZee3%oR>!N,(7<,!cyaX>Qta3%e7(nrB7+a42R-8pJ@(>Qm<IJ+(Sn,e3J+:VN-;(y,$Zj4yZ74[S<FZ:ee49ntV(%Xn3Lr19Y9[7iR3:74X7RV(6ye(Iea1cNSS3gFf!N91%R-ppQQmk79-6%)<SQf-I_@F6_y6SjzB6_!Q2bREIgF:aQdE!KF6>%X<2p@62%!F2bR62<-$2i-)2i-FI%d@G<-@!jy$2iRQ6b-v!jyvIg!:ajzvaj@Fpg!$2b-FIp9;9K-:>Q4:G_@@9Qdv6<!F6<dFI7yFI7yFI7Ll";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Auto-generated by devextreme-license.
|
|
2
2
|
// Do not commit this file to source control.
|
|
3
|
-
export const licenseKey = 'LCPv1(
|
|
3
|
+
export const licenseKey = 'LCPv1>7eY(j4LeYSi9_V6!J4He7,1:JfoSYV:%QDL7+aJZee3%oR>!N,(7<,!cyaX>Qta3%e7(nrB7+a42R-8pJ@(>Qm<IJ+(Sn,e3J+:VN-;(y,$Zj4yZ74[S<FZ:ee49ntV(%Xn3Lr19Y9[7iR3:74X7RV(6ye(Iea1cNSS3gFf!N91%R-ppQQmk79-6%)<SQf-I_@F6_y6SjzB6_!Q2bREIgF:aQdE!KF6>%X<2p@62%!F2bR62<-$2i-)2i-FI%d@G<-@!jy$2iRQ6b-v!jyvIg!:ajzvaj@Fpg!$2b-FIp9;9K-:>Q4:G_@@9Qdv6<!F6<dFI7yFI7yFI7Ll';
|
|
@@ -152,6 +152,15 @@ export type MultiDetailPanelState = {
|
|
|
152
152
|
};
|
|
153
153
|
/** Stato dei pannelli del riepilogo, indicizzato per id pannello */
|
|
154
154
|
export type MultiDetailPanelStates = Record<string, MultiDetailPanelState>;
|
|
155
|
+
/** Passi dell'archiviazione multipla di cui si ricordano le dimensioni del modale */
|
|
156
|
+
export type MultiDetailModalStep = 'selectDcmtType' | 'archiveSummary';
|
|
157
|
+
/** Dimensioni salvate del modale, in formato CSS (es. '1200px') */
|
|
158
|
+
export type MultiDetailModalSize = {
|
|
159
|
+
width: string;
|
|
160
|
+
height: string;
|
|
161
|
+
};
|
|
162
|
+
/** Dimensioni del modale indicizzate per passo */
|
|
163
|
+
export type MultiDetailModalSizes = Partial<Record<MultiDetailModalStep, MultiDetailModalSize>>;
|
|
155
164
|
export declare class ArchivingSettings {
|
|
156
165
|
mruTIDs: number[];
|
|
157
166
|
defaultTree: number;
|
|
@@ -165,6 +174,11 @@ export declare class ArchivingSettings {
|
|
|
165
174
|
* sezioni, che è in multiDetailSectionsOpen: assente finché l'utente non sposta un separatore.
|
|
166
175
|
*/
|
|
167
176
|
multiDetailPanelStates?: MultiDetailPanelStates;
|
|
177
|
+
/**
|
|
178
|
+
* Dimensioni del modale scelte dall'utente, distinte per passo (scelta del tipo e riepilogo partono
|
|
179
|
+
* da dimensioni molto diverse). Assenti finché non ridimensiona il modale in quel passo.
|
|
180
|
+
*/
|
|
181
|
+
multiDetailModalSizes?: MultiDetailModalSizes;
|
|
168
182
|
}
|
|
169
183
|
/** Stato di apertura salvato delle sezioni: i default coprono l'impostazione mai modificata dall'utente */
|
|
170
184
|
export declare const getMultiDetailSectionsOpen: () => MultiDetailSectionsOpen;
|
|
@@ -174,11 +188,16 @@ export declare const getMultiDetailPanelStates: () => MultiDetailPanelStates;
|
|
|
174
188
|
/** Il panel manager applica lo stato salvato solo se esiste: altrimenti riparte dalle quote di default */
|
|
175
189
|
export declare const hasMultiDetailPanelStates: () => boolean;
|
|
176
190
|
export declare const saveMultiDetailPanelStates: (panelStates: MultiDetailPanelStates) => void;
|
|
177
|
-
/**
|
|
191
|
+
/** Assenti se l'utente non ha mai ridimensionato il modale in quel passo */
|
|
192
|
+
export declare const getMultiDetailModalSize: (step: MultiDetailModalStep) => MultiDetailModalSize | undefined;
|
|
193
|
+
export declare const hasMultiDetailModalSizes: () => boolean;
|
|
194
|
+
/** Salva le dimensioni del passo indicato, lasciando invariate quelle degli altri */
|
|
195
|
+
export declare const saveMultiDetailModalSize: (step: MultiDetailModalStep, size: MultiDetailModalSize) => void;
|
|
196
|
+
/** C'è un layout dell'utente da cancellare: nel local storage le impostazioni ci sono solo se diverse dai default */
|
|
178
197
|
export declare const hasMultiDetailLayout: () => boolean;
|
|
179
198
|
/**
|
|
180
|
-
* Elimina dal local storage il layout configurato dall'utente
|
|
181
|
-
*
|
|
199
|
+
* Elimina dal local storage il layout configurato dall'utente: altezze dei pannelli, sezioni
|
|
200
|
+
* collassate e dimensioni del modale. Tutto torna ai default: modale, quote e sezioni espanse.
|
|
182
201
|
*/
|
|
183
202
|
export declare const clearMultiDetailLayout: () => void;
|
|
184
203
|
export declare class FullTextSettings {
|
|
@@ -260,17 +260,31 @@ export const saveMultiDetailPanelStates = (panelStates) => {
|
|
|
260
260
|
// Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero
|
|
261
261
|
SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = { ...panelStates };
|
|
262
262
|
};
|
|
263
|
-
/**
|
|
264
|
-
export const
|
|
263
|
+
/** Assenti se l'utente non ha mai ridimensionato il modale in quel passo */
|
|
264
|
+
export const getMultiDetailModalSize = (step) => SDKUI_Globals.userSettings.archivingSettings?.multiDetailModalSizes?.[step];
|
|
265
|
+
export const hasMultiDetailModalSizes = () => Object.keys(SDKUI_Globals.userSettings.archivingSettings?.multiDetailModalSizes ?? {}).length > 0;
|
|
266
|
+
/** Salva le dimensioni del passo indicato, lasciando invariate quelle degli altri */
|
|
267
|
+
export const saveMultiDetailModalSize = (step, size) => {
|
|
268
|
+
if (!size?.width || !size?.height)
|
|
269
|
+
return;
|
|
270
|
+
// Il proxy persiste solo intercettando l'assegnazione di una proprietà: la mappa va riassegnata per intero
|
|
271
|
+
SDKUI_Globals.userSettings.archivingSettings.multiDetailModalSizes = {
|
|
272
|
+
...SDKUI_Globals.userSettings.archivingSettings?.multiDetailModalSizes,
|
|
273
|
+
[step]: { ...size },
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
/** C'è un layout dell'utente da cancellare: nel local storage le impostazioni ci sono solo se diverse dai default */
|
|
277
|
+
export const hasMultiDetailLayout = () => hasMultiDetailPanelStates() || hasMultiDetailModalSizes() || SDKUI_Globals.userSettings.archivingSettings?.multiDetailSectionsOpen !== undefined;
|
|
265
278
|
/**
|
|
266
|
-
* Elimina dal local storage il layout configurato dall'utente
|
|
267
|
-
*
|
|
279
|
+
* Elimina dal local storage il layout configurato dall'utente: altezze dei pannelli, sezioni
|
|
280
|
+
* collassate e dimensioni del modale. Tutto torna ai default: modale, quote e sezioni espanse.
|
|
268
281
|
*/
|
|
269
282
|
export const clearMultiDetailLayout = () => {
|
|
270
|
-
// Il proxy persiste solo intercettando l'assegnazione di una proprietà:
|
|
283
|
+
// Il proxy persiste solo intercettando l'assegnazione di una proprietà: tutti i campi vanno riassegnati.
|
|
271
284
|
// Senza valore spariscono dal local storage e alla riapertura tornano validi i default
|
|
272
285
|
SDKUI_Globals.userSettings.archivingSettings.multiDetailPanelStates = undefined;
|
|
273
286
|
SDKUI_Globals.userSettings.archivingSettings.multiDetailSectionsOpen = undefined;
|
|
287
|
+
SDKUI_Globals.userSettings.archivingSettings.multiDetailModalSizes = undefined;
|
|
274
288
|
};
|
|
275
289
|
export class FullTextSettings {
|
|
276
290
|
constructor() {
|
|
@@ -172,6 +172,12 @@ export declare class SDKUI_Localizator {
|
|
|
172
172
|
static get DcmtTypeSelectOrQuickSearch(): "Wählen Sie einen Dokumenttyp oder eine Schnellsuche aus" | "Select a document type or quick search" | "Seleccione un tipo de documento o búsqueda rápida" | "Sélectionnez un type de document ou une recherche rapide" | "Selecione um tipo de documento ou pesquisa rápida" | "Selezionare un tipo documento o ricerca rapida";
|
|
173
173
|
static get Default(): "Standard" | "Default" | "Predeterminado" | "Defaultão";
|
|
174
174
|
static get DefaultFolder(): string;
|
|
175
|
+
static get DcmtTypeStructureNotRetrieved(): "Die Struktur des Dokumenttyps konnte nicht abgerufen werden." | "Unable to retrieve the document type structure." | "No se ha podido recuperar la estructura del tipo de documento." | "Impossible de récupérer la structure du type de document." | "Não foi possível recuperar a estrutura do tipo de documento." | "Non è stato possibile recuperare la struttura del tipo documento.";
|
|
176
|
+
static get DetailCopiedToArchiveList(): "Detail zu den zu archivierenden Details kopiert" | "Detail copied to the details to archive" | "Detalle copiado a los detalles a archivar" | "Détail copié dans les détails à archiver" | "Detalhe copiado para os detalhes a arquivar" | "Dettaglio copiato tra i dettagli da archiviare";
|
|
177
|
+
static get DetailFileNotRetrieved(): "Datei des Details nicht abgerufen" | "Detail file not retrieved" | "Archivo del detalle no recuperado" | "Fichier du détail non récupéré" | "Ficheiro do detalhe não recuperado" | "File del dettaglio non recuperato";
|
|
178
|
+
static get DetailFileToAddFromRowForm(): "Der Dokumenttyp erfordert eine Datei: Fügen Sie sie über das Formular der Zeile hinzu." | "The document type requires a file: add it from the row form." | "El tipo de documento requiere un archivo: añádelo desde el formulario de la fila." | "Le type de document exige un fichier : ajoutez-le depuis le formulaire de la ligne." | "O tipo de documento exige um ficheiro: adicione-o a partir do formulário da linha." | "Il tipo documento richiede un file: aggiungilo dal form della riga.";
|
|
179
|
+
static get DetailMetadataNotRetrieved(): "Die Metadaten des Details konnten nicht abgerufen werden." | "Unable to retrieve the detail metadata." | "No se han podido recuperar los metadatos del detalle." | "Impossible de récupérer les métadonnées du détail." | "Não foi possível recuperar os metadados do detalhe." | "Non è stato possibile recuperare i metadati del dettaglio.";
|
|
180
|
+
static get DetailNotOfSelectedDcmtType(): "Das Detail gehört nicht zum ausgewählten Dokumenttyp." | "The detail does not belong to the selected document type." | "El detalle no pertenece al tipo de documento seleccionado." | "Le détail n'appartient pas au type de document sélectionné." | "O detalhe não pertence ao tipo de documento selecionado." | "Il dettaglio non è del tipo documento selezionato.";
|
|
175
181
|
static get Details(): "Einzelheiten" | "Details" | "Detalles" | "Détails" | "detalhes" | "Dettagli";
|
|
176
182
|
static get DetailsToArchive(): string;
|
|
177
183
|
static get Delete(): "Löschen" | "Delete" | "Borrar" | "Supprimer" | "Excluir" | "Elimina";
|
|
@@ -454,6 +460,7 @@ export declare class SDKUI_Localizator {
|
|
|
454
460
|
static get LastUpdateTime(): "Letzte Änderung" | "Last update Time" | "Última modificación" | "Dernière modifie" | "Última modificação" | "Ultima modifica";
|
|
455
461
|
static get LastVersion(): string;
|
|
456
462
|
static get Latest(): string;
|
|
463
|
+
static get LayoutRemoved(): string;
|
|
457
464
|
static get LexProt(): "Lex-Schutz" | "Lex protection" | "Protección Lex" | "Protection Lex" | "Proteção Lex" | "Protezione Lex";
|
|
458
465
|
static get Line(): "Linie" | "Line" | "Línea" | "Ligne" | "Linha" | "Linea";
|
|
459
466
|
static get List(): "Liste" | "List" | "Lista";
|
|
@@ -690,6 +697,7 @@ export declare class SDKUI_Localizator {
|
|
|
690
697
|
static get RemoveFromWorkgroup(): string;
|
|
691
698
|
static get RemoveNamedPreferredCredentials(): "Möchten Sie die Anmeldedaten '{{0}}' entfernen, die als bevorzugt festgelegt wurden?" | "Do you want to remove the '{{0}}' credentials set as preferred?" | "¿Quieres eliminar las credenciales '{{0}}' configuradas como preferidas?" | "Voulez-vous supprimer les identifiants '{{0}}' définis comme préférés ?" | "Deseja remover as credenciais '{{0}}' definidas como preferidas?" | "Vuoi rimuovere le credenziali '{{0}}' impostate come preferite?";
|
|
692
699
|
static get RemoveLayout(): string;
|
|
700
|
+
static get RemoveLayout_Confirm(): string;
|
|
693
701
|
static get RemovingFromList(): string;
|
|
694
702
|
static get RemoveSignatureIfPresent(): string;
|
|
695
703
|
static get RememberCredentials(): "Anmeldedaten merken" | "Remember credentials" | "Recordar credenciales" | "Se souvenir des identifiants" | "Lembrar credenciais" | "Ricorda credenziali";
|
|
@@ -970,6 +978,7 @@ export declare class SDKUI_Localizator {
|
|
|
970
978
|
static get WrittenOn(): "Geschrieben am" | "Written on" | "Escrito el" | "Écrit le" | "Escrito em" | "Scritto il";
|
|
971
979
|
static get YouDoNotHavePermissionsToArchiveDetailDocumentsOfThisType(): "Sie haben keine Berechtigung, Detaildokumente dieses Typs zu archivieren." | "You do not have permissions to archive detail documents of this type." | "No tienes permisos para archivar documentos de detalle de este tipo." | "Vous n'avez pas les permissions pour archiver les documents détail de ce type." | "Você não tem permissões para arquivar documentos de detalhe deste tipo." | "Non hai i permessi per archiviare documenti di dettaglio di questo tipo.";
|
|
972
980
|
static get YouDoNotHavePermissionsToArchiveMasterDocumentsOfThisType(): "Sie haben keine Berechtigung, Master-Dokumente dieses Typs zu archivieren." | "You do not have permissions to archive master documents of this type." | "No tienes permisos para archivar documentos maestros de este tipo." | "Vous n'avez pas les permissions pour archiver les documents maîtres de ce type." | "Você não tem permissões para arquivar documentos mestres deste tipo." | "Non hai i permessi per archiviare documenti master di questo tipo.";
|
|
981
|
+
static get YouDoNotHavePermissionsToRetrieveTheFile(): "Sie haben keine Berechtigung, die Datei abzurufen." | "You do not have permissions to retrieve the file." | "No tienes permisos para recuperar el archivo." | "Vous n'avez pas les permissions pour récupérer le fichier." | "Você não tem permissões para recuperar o ficheiro." | "Non hai i permessi per recuperare il file.";
|
|
973
982
|
static get Yes(): "Ja" | "Yes" | "Sí" | "Oui" | "Sim" | "Sì";
|
|
974
983
|
static get ZipFileName(): string;
|
|
975
984
|
static get ZipCreatedSavedInFolder(): string;
|
|
@@ -1712,6 +1712,66 @@ export class SDKUI_Localizator {
|
|
|
1712
1712
|
return "Cartella di default";
|
|
1713
1713
|
}
|
|
1714
1714
|
}
|
|
1715
|
+
static get DcmtTypeStructureNotRetrieved() {
|
|
1716
|
+
switch (this._cultureID) {
|
|
1717
|
+
case CultureIDs.De_DE: return "Die Struktur des Dokumenttyps konnte nicht abgerufen werden.";
|
|
1718
|
+
case CultureIDs.En_US: return "Unable to retrieve the document type structure.";
|
|
1719
|
+
case CultureIDs.Es_ES: return "No se ha podido recuperar la estructura del tipo de documento.";
|
|
1720
|
+
case CultureIDs.Fr_FR: return "Impossible de récupérer la structure du type de document.";
|
|
1721
|
+
case CultureIDs.Pt_PT: return "Não foi possível recuperar a estrutura do tipo de documento.";
|
|
1722
|
+
default: return "Non è stato possibile recuperare la struttura del tipo documento.";
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
static get DetailCopiedToArchiveList() {
|
|
1726
|
+
switch (this._cultureID) {
|
|
1727
|
+
case CultureIDs.De_DE: return "Detail zu den zu archivierenden Details kopiert";
|
|
1728
|
+
case CultureIDs.En_US: return "Detail copied to the details to archive";
|
|
1729
|
+
case CultureIDs.Es_ES: return "Detalle copiado a los detalles a archivar";
|
|
1730
|
+
case CultureIDs.Fr_FR: return "Détail copié dans les détails à archiver";
|
|
1731
|
+
case CultureIDs.Pt_PT: return "Detalhe copiado para os detalhes a arquivar";
|
|
1732
|
+
default: return "Dettaglio copiato tra i dettagli da archiviare";
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
static get DetailFileNotRetrieved() {
|
|
1736
|
+
switch (this._cultureID) {
|
|
1737
|
+
case CultureIDs.De_DE: return "Datei des Details nicht abgerufen";
|
|
1738
|
+
case CultureIDs.En_US: return "Detail file not retrieved";
|
|
1739
|
+
case CultureIDs.Es_ES: return "Archivo del detalle no recuperado";
|
|
1740
|
+
case CultureIDs.Fr_FR: return "Fichier du détail non récupéré";
|
|
1741
|
+
case CultureIDs.Pt_PT: return "Ficheiro do detalhe não recuperado";
|
|
1742
|
+
default: return "File del dettaglio non recuperato";
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
static get DetailFileToAddFromRowForm() {
|
|
1746
|
+
switch (this._cultureID) {
|
|
1747
|
+
case CultureIDs.De_DE: return "Der Dokumenttyp erfordert eine Datei: Fügen Sie sie über das Formular der Zeile hinzu.";
|
|
1748
|
+
case CultureIDs.En_US: return "The document type requires a file: add it from the row form.";
|
|
1749
|
+
case CultureIDs.Es_ES: return "El tipo de documento requiere un archivo: añádelo desde el formulario de la fila.";
|
|
1750
|
+
case CultureIDs.Fr_FR: return "Le type de document exige un fichier : ajoutez-le depuis le formulaire de la ligne.";
|
|
1751
|
+
case CultureIDs.Pt_PT: return "O tipo de documento exige um ficheiro: adicione-o a partir do formulário da linha.";
|
|
1752
|
+
default: return "Il tipo documento richiede un file: aggiungilo dal form della riga.";
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
static get DetailMetadataNotRetrieved() {
|
|
1756
|
+
switch (this._cultureID) {
|
|
1757
|
+
case CultureIDs.De_DE: return "Die Metadaten des Details konnten nicht abgerufen werden.";
|
|
1758
|
+
case CultureIDs.En_US: return "Unable to retrieve the detail metadata.";
|
|
1759
|
+
case CultureIDs.Es_ES: return "No se han podido recuperar los metadatos del detalle.";
|
|
1760
|
+
case CultureIDs.Fr_FR: return "Impossible de récupérer les métadonnées du détail.";
|
|
1761
|
+
case CultureIDs.Pt_PT: return "Não foi possível recuperar os metadados do detalhe.";
|
|
1762
|
+
default: return "Non è stato possibile recuperare i metadati del dettaglio.";
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
static get DetailNotOfSelectedDcmtType() {
|
|
1766
|
+
switch (this._cultureID) {
|
|
1767
|
+
case CultureIDs.De_DE: return "Das Detail gehört nicht zum ausgewählten Dokumenttyp.";
|
|
1768
|
+
case CultureIDs.En_US: return "The detail does not belong to the selected document type.";
|
|
1769
|
+
case CultureIDs.Es_ES: return "El detalle no pertenece al tipo de documento seleccionado.";
|
|
1770
|
+
case CultureIDs.Fr_FR: return "Le détail n'appartient pas au type de document sélectionné.";
|
|
1771
|
+
case CultureIDs.Pt_PT: return "O detalhe não pertence ao tipo de documento selecionado.";
|
|
1772
|
+
default: return "Il dettaglio non è del tipo documento selezionato.";
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1715
1775
|
static get Details() {
|
|
1716
1776
|
switch (this._cultureID) {
|
|
1717
1777
|
case CultureIDs.De_DE: return "Einzelheiten";
|
|
@@ -4508,6 +4568,16 @@ export class SDKUI_Localizator {
|
|
|
4508
4568
|
default: return "Ultimi";
|
|
4509
4569
|
}
|
|
4510
4570
|
}
|
|
4571
|
+
static get LayoutRemoved() {
|
|
4572
|
+
switch (this._cultureID) {
|
|
4573
|
+
case CultureIDs.De_DE: return "Layout entfernt";
|
|
4574
|
+
case CultureIDs.En_US: return "Layout removed";
|
|
4575
|
+
case CultureIDs.Es_ES: return "Diseño eliminado";
|
|
4576
|
+
case CultureIDs.Fr_FR: return "Mise en page supprimée";
|
|
4577
|
+
case CultureIDs.Pt_PT: return "Layout removido";
|
|
4578
|
+
default: return "Layout rimosso";
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4511
4581
|
static get LexProt() {
|
|
4512
4582
|
switch (this._cultureID) {
|
|
4513
4583
|
case CultureIDs.De_DE: return "Lex-Schutz";
|
|
@@ -6879,6 +6949,16 @@ export class SDKUI_Localizator {
|
|
|
6879
6949
|
default: return "Rimuovi Layout";
|
|
6880
6950
|
}
|
|
6881
6951
|
}
|
|
6952
|
+
static get RemoveLayout_Confirm() {
|
|
6953
|
+
switch (this._cultureID) {
|
|
6954
|
+
case CultureIDs.De_DE: return "Möchten Sie das Layout entfernen?";
|
|
6955
|
+
case CultureIDs.En_US: return "Do you want to remove the layout?";
|
|
6956
|
+
case CultureIDs.Es_ES: return "¿Desea eliminar el diseño?";
|
|
6957
|
+
case CultureIDs.Fr_FR: return "Voulez-vous supprimer la mise en page ?";
|
|
6958
|
+
case CultureIDs.Pt_PT: return "Deseja remover o layout?";
|
|
6959
|
+
default: return "Vuoi rimuovere il layout?";
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6882
6962
|
static get RemovingFromList() {
|
|
6883
6963
|
switch (this._cultureID) {
|
|
6884
6964
|
case CultureIDs.De_DE: return "Wird aus der Liste entfernt";
|
|
@@ -9692,6 +9772,16 @@ export class SDKUI_Localizator {
|
|
|
9692
9772
|
default: return "Non hai i permessi per archiviare documenti master di questo tipo.";
|
|
9693
9773
|
}
|
|
9694
9774
|
}
|
|
9775
|
+
static get YouDoNotHavePermissionsToRetrieveTheFile() {
|
|
9776
|
+
switch (this._cultureID) {
|
|
9777
|
+
case CultureIDs.De_DE: return "Sie haben keine Berechtigung, die Datei abzurufen.";
|
|
9778
|
+
case CultureIDs.En_US: return "You do not have permissions to retrieve the file.";
|
|
9779
|
+
case CultureIDs.Es_ES: return "No tienes permisos para recuperar el archivo.";
|
|
9780
|
+
case CultureIDs.Fr_FR: return "Vous n'avez pas les permissions pour récupérer le fichier.";
|
|
9781
|
+
case CultureIDs.Pt_PT: return "Você não tem permissões para recuperar o ficheiro.";
|
|
9782
|
+
default: return "Non hai i permessi per recuperare il file.";
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9695
9785
|
static get Yes() {
|
|
9696
9786
|
switch (this._cultureID) {
|
|
9697
9787
|
case CultureIDs.De_DE: return "Ja";
|
|
@@ -110,6 +110,8 @@ export interface UseDocumentOperationsResult {
|
|
|
110
110
|
renderDcmtOperations: React.ReactNode;
|
|
111
111
|
features: {
|
|
112
112
|
isOpenDcmtForm: boolean;
|
|
113
|
+
/** Il form del documento è aperto come popup sopra il contenuto, che resta visibile sotto */
|
|
114
|
+
isDcmtFormModal: boolean;
|
|
113
115
|
openFormHandler: (layoutMode: LayoutModes) => void;
|
|
114
116
|
dcmtFormLayoutMode: LayoutModes;
|
|
115
117
|
onDcmtFormOpenChange: (isOpen: boolean, layoutMode: LayoutModes) => void;
|
|
@@ -150,6 +150,12 @@ export const useDocumentOperations = (props) => {
|
|
|
150
150
|
const [showFloatingBar, setShowFloatingBar] = useState(allowFloatingBar);
|
|
151
151
|
const [isOpenDcmtForm, setIsOpenDcmtForm] = useState(false);
|
|
152
152
|
const [dcmtFormLayoutMode, setDcmtFormLayoutMode] = useState(LayoutModes.Update);
|
|
153
|
+
/**
|
|
154
|
+
* Il form del documento è a popup: si sovrappone a chi lo ha aperto, che resta visibile sotto e non
|
|
155
|
+
* va nascosto. Oltre ai contesti che lo chiedono (openDcmtFormAsModal) è il caso della duplicazione,
|
|
156
|
+
* che parte da un documento a video (LayoutModes.Ark con un DID) per proporne uno nuovo da archiviare.
|
|
157
|
+
*/
|
|
158
|
+
const isDcmtFormModal = openDcmtFormAsModal || (dcmtFormLayoutMode === LayoutModes.Ark && focusedItem?.DID !== undefined);
|
|
153
159
|
const [currentCustomButton, setCurrentCustomButton] = useState();
|
|
154
160
|
// State to control the visibility of the details form
|
|
155
161
|
const [showSearchTMDatagrid, setShowSearchTMDatagrid] = useState(false);
|
|
@@ -1393,8 +1399,8 @@ export const useDocumentOperations = (props) => {
|
|
|
1393
1399
|
dcmtUtility,
|
|
1394
1400
|
fetchRemoteCertificates
|
|
1395
1401
|
};
|
|
1396
|
-
const renderDcmtOperations = (_jsxs(_Fragment, { children: [(showExportForm && searchResult && dataColumns && dataSource && selectedRowKeys) && (_jsx(TMDataGridExportForm, { dataColumns: dataColumns, dataSource: dataSource, selectedRowKeys: selectedRowKeys, onCloseExportForm: () => setShowExportForm(false), searchResult: searchResult })), _jsx(StyledMultiViewPanel, { "$isVisible": isOpenDcmtForm, children: ((isOpenDcmtForm && focusedItem?.TID !== undefined && focusedItem?.DID !== undefined) &&
|
|
1397
|
-
_jsx(TMDcmtForm, { isModal:
|
|
1402
|
+
const renderDcmtOperations = (_jsxs(_Fragment, { children: [(showExportForm && searchResult && dataColumns && dataSource && selectedRowKeys) && (_jsx(TMDataGridExportForm, { dataColumns: dataColumns, dataSource: dataSource, selectedRowKeys: selectedRowKeys, onCloseExportForm: () => setShowExportForm(false), searchResult: searchResult })), _jsx(StyledMultiViewPanel, { "$isVisible": isOpenDcmtForm && !isDcmtFormModal, children: ((isOpenDcmtForm && focusedItem?.TID !== undefined && focusedItem?.DID !== undefined) &&
|
|
1403
|
+
_jsx(TMDcmtForm, { isModal: isDcmtFormModal, titleModal: dtd?.name ?? '', TID: focusedItem.TID, DID: focusedItem.DID, allowButtonsRefs: true, showTodoDcmtForm: showTodoDcmtForm, layoutMode: dcmtFormLayoutMode, count: visibleItems?.length, itemIndex: visibleItems ? visibleItems.findIndex(o => o.rowIndex === focusedItem?.rowIndex) + 1 : undefined, canNext: canNavigateHandler ? canNavigateHandler('next') : false, canPrev: canNavigateHandler ? canNavigateHandler('prev') : false, onNext: () => onNavigateHandler && onNavigateHandler('next'), onPrev: () => onNavigateHandler && onNavigateHandler('prev'), onClose: () => { (false); onDcmtFormOpenChange(false, LayoutModes.Update); }, onWFOperationCompleted: onWFOperationCompleted, onTaskCreateRequest: onTaskCreateRequest, onSavedAsyncCallback: onSavedAsyncCallback, openS4TViewer: openS4TViewer, onOpenS4TViewerRequest: onOpenS4TViewerRequest, editPdfForm: editPdfForm, onOpenPdfEditorRequest: onOpenPdfEditorRequest, openFileUploaderPdfEditor: openFileUploaderPdfEditor, onReferenceClick: onReferenceClick, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, moreInfoTasks: getMoreInfoTasksForDocument(allTasks, focusedItem?.TID, focusedItem?.DID), showDcmtFormSidebar: showDcmtFormSidebar, datagridUtility: {
|
|
1398
1404
|
onRefreshSearchAsyncDatagrid,
|
|
1399
1405
|
onRefreshDataRowsAsync,
|
|
1400
1406
|
refreshFocusedDataRowAsync,
|
|
@@ -1472,6 +1478,7 @@ export const useDocumentOperations = (props) => {
|
|
|
1472
1478
|
renderDcmtOperations,
|
|
1473
1479
|
features: {
|
|
1474
1480
|
isOpenDcmtForm,
|
|
1481
|
+
isDcmtFormModal,
|
|
1475
1482
|
openFormHandler,
|
|
1476
1483
|
dcmtFormLayoutMode,
|
|
1477
1484
|
onDcmtFormOpenChange,
|
|
@@ -91,6 +91,11 @@ export declare const useMultiMasterDetailDcmts: ({ dtd, masterDcmt, onClose }: U
|
|
|
91
91
|
duplicateRow: (row: ArchiveRow | undefined) => void;
|
|
92
92
|
upsertRow: (values: Array<MetadataValueDescriptorEx>, file: File | undefined) => void;
|
|
93
93
|
updateRowValues: (rowId: number, changes: ArchiveRowChanges) => void;
|
|
94
|
+
copyArchivedDetailToRows: (dcmt: {
|
|
95
|
+
TID?: number | string;
|
|
96
|
+
DID?: number | string;
|
|
97
|
+
FILEEXT?: string;
|
|
98
|
+
} | undefined) => Promise<void>;
|
|
94
99
|
stopOnFirstError: boolean;
|
|
95
100
|
setStopOnFirstError: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
96
101
|
isArchiving: boolean;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
-
import { ArchiveConstraints, ArchiveEngineByID, MetadataDataDomains, MetadataDataTypes, RelationCacheService, RelationTypes, ResultTypes, SDK_Globals } from '@topconsultnpm/sdk-ts';
|
|
2
|
+
import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtOpers, GeneralRetrieveFormats, MetadataDataDomains, MetadataDataTypes, RelationCacheService, RelationTypes, ResultTypes, RetrieveFileOptions, SDK_Globals, SystemMIDsAsNumber } from '@topconsultnpm/sdk-ts';
|
|
3
3
|
import { MetadataValueDescriptorEx } from '../ts';
|
|
4
|
-
import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, hasMultiDetailLayout, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
|
|
4
|
+
import { clearMultiDetailLayout, Globalization, getExceptionMessage, getMultiDetailSectionsOpen, handleArchiveVisibility, hasMultiDetailLayout, saveMultiDetailSectionsOpen, SDKUI_Localizator } from '../helper';
|
|
5
5
|
import { getDcmtMetadataByMid, getUserMetadataSorted, mapAssociationsFromMetadata } from '../helper/dcmtsHelper';
|
|
6
6
|
import { ButtonNames, TMExceptionBoxManager, TMMessageBoxManager } from '../components/base/TMPopUp';
|
|
7
7
|
import TMSpinner from '../components/base/TMSpinner';
|
|
8
8
|
import ShowAlert from '../components/base/TMAlert';
|
|
9
9
|
import { TMResultManager } from '../components/forms/TMResultDialog';
|
|
10
10
|
import { useDataListItem } from './useDataListItem';
|
|
11
|
-
import { buildColumnsFromDtd, canArchive, canUpdateMasterCallback, getDetailDtdAsync, toGridNumber } from '../components/features/documents/TMMultiMasterDetailDcmtsUtils';
|
|
11
|
+
import { buildColumnsFromDtd, canArchive, canUpdateMasterCallback, getDetailDtdAsync, toArchiveRowValue, toGridNumber } from '../components/features/documents/TMMultiMasterDetailDcmtsUtils';
|
|
12
12
|
/** Fasi del flusso di archiviazione multipla dei dettagli */
|
|
13
13
|
export var MultiDetailArchivePhase;
|
|
14
14
|
(function (MultiDetailArchivePhase) {
|
|
@@ -65,6 +65,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
65
65
|
const archiveRowsRef = useRef(archiveRows);
|
|
66
66
|
archiveRowsRef.current = archiveRows;
|
|
67
67
|
const rowIdRef = useRef(0);
|
|
68
|
+
// Copia di un dettaglio archiviato in corso: i clic successivi sull'icona vanno ignorati (vedi copyArchivedDetailToRows)
|
|
69
|
+
const isCopyingArchivedDetailRef = useRef(false);
|
|
68
70
|
const [focusedRowKey, setFocusedRowKey] = useState(undefined);
|
|
69
71
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
70
72
|
// Form di inserimento/modifica di una riga
|
|
@@ -213,11 +215,12 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
213
215
|
setIsLoadingArchivedDetails(false);
|
|
214
216
|
}
|
|
215
217
|
};
|
|
216
|
-
// I risultati seguono il tipo documento selezionato
|
|
218
|
+
// I risultati seguono il tipo documento selezionato e si caricano anche a sezione chiusa: il contatore
|
|
219
|
+
// nella testata e nella barra dei pannelli deve essere corretto senza dover aprire la sezione
|
|
217
220
|
useEffect(() => {
|
|
218
221
|
setArchivedDetailResults([]);
|
|
219
222
|
areArchivedDetailsLoadedRef.current = false;
|
|
220
|
-
if (selectedRelation?.detailTID !== undefined
|
|
223
|
+
if (selectedRelation?.detailTID !== undefined)
|
|
221
224
|
refreshArchivedDetailsAsync();
|
|
222
225
|
}, [selectedRelation?.detailTID]);
|
|
223
226
|
// =============================================================================================
|
|
@@ -229,7 +232,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
229
232
|
saveMultiDetailSectionsOpen(next);
|
|
230
233
|
// Riaperte tutte le sezioni il collasso torna ai default: senza altezze salvate non c'è più layout
|
|
231
234
|
setHasSavedLayout(hasMultiDetailLayout());
|
|
232
|
-
// I dettagli già archiviati si
|
|
235
|
+
// I dettagli già archiviati sono caricati insieme al tipo di dettaglio: qui si riprova solo se quel caricamento è fallito
|
|
233
236
|
if (section === 'archivedDetails' && isOpen && !areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
234
237
|
refreshArchivedDetailsAsync();
|
|
235
238
|
};
|
|
@@ -241,7 +244,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
241
244
|
*/
|
|
242
245
|
const openAllSections = () => {
|
|
243
246
|
setSectionsOpen(prev => Object.values(prev).every(Boolean) ? prev : { masterInfo: true, archivedDetails: true, archiveList: true });
|
|
244
|
-
// I dettagli già archiviati si
|
|
247
|
+
// I dettagli già archiviati sono caricati insieme al tipo di dettaglio: qui si riprova solo se quel caricamento è fallito
|
|
245
248
|
if (!areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
246
249
|
refreshArchivedDetailsAsync();
|
|
247
250
|
};
|
|
@@ -251,8 +254,8 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
251
254
|
*/
|
|
252
255
|
const clearLayout = () => {
|
|
253
256
|
TMMessageBoxManager.show({
|
|
254
|
-
title:
|
|
255
|
-
message:
|
|
257
|
+
title: SDKUI_Localizator.RemoveLayout,
|
|
258
|
+
message: SDKUI_Localizator.RemoveLayout_Confirm,
|
|
256
259
|
buttons: [ButtonNames.YES, ButtonNames.NO],
|
|
257
260
|
onButtonClick: (button) => {
|
|
258
261
|
if (button !== ButtonNames.YES)
|
|
@@ -262,10 +265,10 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
262
265
|
setSectionsOpen(restored);
|
|
263
266
|
setLayoutResetToken(prev => prev + 1);
|
|
264
267
|
setHasSavedLayout(false);
|
|
265
|
-
//
|
|
266
|
-
if (
|
|
268
|
+
// I dettagli già archiviati sono caricati insieme al tipo di dettaglio: qui si riprova solo se quel caricamento è fallito
|
|
269
|
+
if (!areArchivedDetailsLoadedRef.current && !isLoadingArchivedDetails)
|
|
267
270
|
refreshArchivedDetailsAsync();
|
|
268
|
-
ShowAlert({ message:
|
|
271
|
+
ShowAlert({ message: SDKUI_Localizator.LayoutRemoved, mode: 'success', title: SDKUI_Localizator.RemoveLayout, duration: 3000 });
|
|
269
272
|
},
|
|
270
273
|
});
|
|
271
274
|
};
|
|
@@ -394,6 +397,128 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
394
397
|
rowIdRef.current += 1;
|
|
395
398
|
setArchiveRows(prev => [...prev, buildArchiveRow(rowIdRef.current, row.__tid, row.__values, row.__file)]);
|
|
396
399
|
}, [buildArchiveRow]);
|
|
400
|
+
/**
|
|
401
|
+
* Copia un dettaglio già archiviato tra le righe da archiviare: i suoi metadati (e il file, se il
|
|
402
|
+
* tipo lo prevede e il documento ne ha uno) diventano una nuova riga del riepilogo. Il documento
|
|
403
|
+
* archiviato non è toccato: quello che si ottiene è un nuovo dettaglio, ancora da archiviare.
|
|
404
|
+
* I numeratori non si copiano, il loro valore lo assegna il sistema all'archiviazione.
|
|
405
|
+
* Ogni motivo che impedisce la copia, o che la rende parziale, arriva all'utente con un avviso:
|
|
406
|
+
* la riga entra nel riepilogo solo se i metadati del dettaglio sono stati letti davvero.
|
|
407
|
+
*/
|
|
408
|
+
const copyArchivedDetailToRows = async (dcmt) => {
|
|
409
|
+
// Clic ripetuti sull'icona: una copia alla volta, altrimenti spinner e righe si sovrappongono
|
|
410
|
+
if (isCopyingArchivedDetailRef.current)
|
|
411
|
+
return;
|
|
412
|
+
try {
|
|
413
|
+
const detailTID = selectedRelation?.detailTID;
|
|
414
|
+
// Senza tipo selezionato il riepilogo non ha colonne in cui copiare il dettaglio
|
|
415
|
+
if (detailTID === undefined) {
|
|
416
|
+
ShowAlert({ message: SDKUI_Localizator.SelectDcmtTypeForDetailArchiving, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 3000 });
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
// La griglia dei risultati porta i valori di riga come stringhe: TID e DID vanno usati come numeri
|
|
420
|
+
const archivedDcmt = { TID: Number(dcmt?.TID), DID: Number(dcmt?.DID) };
|
|
421
|
+
if (!Number.isFinite(archivedDcmt.TID) || !Number.isFinite(archivedDcmt.DID) || archivedDcmt.TID <= 0 || archivedDcmt.DID <= 0) {
|
|
422
|
+
ShowAlert({ message: SDKUI_Localizator.InvalidDcmt, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 3000 });
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
// Le righe del riepilogo sono del tipo selezionato: un dettaglio di altro tipo non ne ha le colonne
|
|
426
|
+
if (archivedDcmt.TID !== detailTID) {
|
|
427
|
+
ShowAlert({ message: SDKUI_Localizator.DetailNotOfSelectedDcmtType, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 3000 });
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
isCopyingArchivedDetailRef.current = true;
|
|
431
|
+
TMSpinner.show({ description: SDKUI_Localizator.Loading });
|
|
432
|
+
const [detailDtd, metadataByMid] = await Promise.all([getDetailDtdAsync(detailTID), getDcmtMetadataByMid(archivedDcmt)]);
|
|
433
|
+
// Senza la struttura del tipo non si sa quali metadati copiare: la riga nascerebbe vuota
|
|
434
|
+
if (!detailDtd) {
|
|
435
|
+
ShowAlert({ message: SDKUI_Localizator.DcmtTypeStructureNotRetrieved, mode: 'error', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// Il permesso serve comunque all'archiviazione: dirlo qui evita di compilare righe inutili
|
|
439
|
+
if (!canArchive(detailDtd)) {
|
|
440
|
+
ShowAlert({ message: SDKUI_Localizator.YouDoNotHavePermissionsToArchiveDetailDocumentsOfThisType, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
// Metadati non letti (sessione non valida, documento non più visibile): una riga di valori vuoti non serve
|
|
444
|
+
if (metadataByMid.size === 0) {
|
|
445
|
+
ShowAlert({ message: SDKUI_Localizator.DetailMetadataNotRetrieved, mode: 'error', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const userMetadata = getUserMetadataSorted(detailDtd);
|
|
449
|
+
const archivableMetadata = userMetadata.filter(md => md.dataDomain !== MetadataDataDomains.Numerator && handleArchiveVisibility(md));
|
|
450
|
+
// Niente da copiare: il tipo non ha metadati utente, o nessuno di questi è archiviabile
|
|
451
|
+
if (archivableMetadata.length === 0) {
|
|
452
|
+
const message = userMetadata.length > 0
|
|
453
|
+
? SDKUI_Localizator.YouDoNotHavePermissionsToArchiveDetailDocumentsOfThisType
|
|
454
|
+
: SDKUI_Localizator.DetailMetadataNotRetrieved;
|
|
455
|
+
ShowAlert({ message, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
// Gli stessi metadati che compila il form della riga: valori come stringhe, mancanti a vuoto
|
|
459
|
+
const values = archivableMetadata.map(md => {
|
|
460
|
+
const mvd = new MetadataValueDescriptorEx();
|
|
461
|
+
mvd.tid = detailTID;
|
|
462
|
+
mvd.mid = md.id;
|
|
463
|
+
mvd.md = md;
|
|
464
|
+
// Date e numeri nel formato del form: la riga copiata si rilegge e si modifica come le altre
|
|
465
|
+
mvd.value = toArchiveRowValue(metadataByMid.get(md.id), md.dataType);
|
|
466
|
+
mvd.isRequired = md.isRequired?.toString();
|
|
467
|
+
return mvd;
|
|
468
|
+
});
|
|
469
|
+
// Il file segue la riga solo se il tipo lo prevede e il documento ne ha uno; se non si
|
|
470
|
+
// recupera la riga si inserisce comunque, il file si aggiunge dal form della riga
|
|
471
|
+
let file = undefined;
|
|
472
|
+
// Il file c'è se lo dice il metadato di sistema del documento: la colonna della griglia
|
|
473
|
+
// dei risultati è solo un ripiego, la ricerca potrebbe non averla tra i suoi valori
|
|
474
|
+
const fileExt = String(metadataByMid.get(SystemMIDsAsNumber.FileExt) ?? dcmt?.FILEEXT ?? '');
|
|
475
|
+
const hasFile = fileExt !== '';
|
|
476
|
+
const supportsFile = detailDtd.archiveConstraint !== ArchiveConstraints.OnlyMetadata;
|
|
477
|
+
if (hasFile && supportsFile) {
|
|
478
|
+
if (detailDtd.perm?.canRetrieveFile !== AccessLevels.Yes) {
|
|
479
|
+
ShowAlert({ message: `${SDKUI_Localizator.DetailFileNotRetrieved}: ${SDKUI_Localizator.YouDoNotHavePermissionsToRetrieveTheFile}`, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
try {
|
|
483
|
+
const rfo = new RetrieveFileOptions();
|
|
484
|
+
rfo.retrieveReason = DcmtOpers.None;
|
|
485
|
+
rfo.generalRetrieveFormat = GeneralRetrieveFormats.OriginalUnsigned;
|
|
486
|
+
file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(archivedDcmt.TID, archivedDcmt.DID, rfo);
|
|
487
|
+
// Nessun errore ma nemmeno il file (sessione non valida): l'utente deve saperlo
|
|
488
|
+
if (!file)
|
|
489
|
+
ShowAlert({ message: SDKUI_Localizator.DetailFileNotRetrieved, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
490
|
+
}
|
|
491
|
+
catch (error) {
|
|
492
|
+
ShowAlert({ message: `${SDKUI_Localizator.DetailFileNotRetrieved}: ${getExceptionMessage(error)}`, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
rowIdRef.current += 1;
|
|
497
|
+
const newRowId = rowIdRef.current;
|
|
498
|
+
setArchiveRows(prev => [...prev, buildArchiveRow(newRowId, detailTID, values, file)]);
|
|
499
|
+
// La riga copiata è quella su cui si lavora: fuoco e selezione la seguono
|
|
500
|
+
setFocusedRowKey(newRowId);
|
|
501
|
+
setSelectedRowKeys([newRowId]);
|
|
502
|
+
// Con la sezione chiusa la riga appena copiata non si vedrebbe
|
|
503
|
+
if (!sectionsOpen.archiveList)
|
|
504
|
+
setSectionOpen('archiveList', true);
|
|
505
|
+
ShowAlert({ message: SDKUI_Localizator.DetailCopiedToArchiveList, mode: 'success', title: SDKUI_Localizator.Duplicate, duration: 3000 });
|
|
506
|
+
// Il tipo pretende il file: senza, la riga non passerebbe l'archiviazione
|
|
507
|
+
if (!file && detailDtd.archiveConstraint === ArchiveConstraints.ContentCompulsory) {
|
|
508
|
+
ShowAlert({ message: SDKUI_Localizator.DetailFileToAddFromRowForm, mode: 'warning', title: SDKUI_Localizator.Duplicate, duration: 5000 });
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
TMExceptionBoxManager.show({ exception: error });
|
|
513
|
+
}
|
|
514
|
+
finally {
|
|
515
|
+
// Spinner e blocco partono insieme: se la copia non è nemmeno iniziata non c'è niente da chiudere
|
|
516
|
+
if (isCopyingArchivedDetailRef.current) {
|
|
517
|
+
isCopyingArchivedDetailRef.current = false;
|
|
518
|
+
TMSpinner.hide();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
397
522
|
// All'apertura del form pre-compila i MID di collegamento valorizzati dal master
|
|
398
523
|
useEffect(() => {
|
|
399
524
|
if (!isOpenAddForm || !selectedRelation) {
|
|
@@ -493,8 +618,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
493
618
|
setArchiveRows(prev => prev.filter(r => !archivedRowIds.has(r.id)));
|
|
494
619
|
setSelectedRowKeys([]);
|
|
495
620
|
setFocusedRowKey(undefined);
|
|
496
|
-
|
|
497
|
-
refreshArchivedDetailsAsync();
|
|
621
|
+
refreshArchivedDetailsAsync();
|
|
498
622
|
}
|
|
499
623
|
const successCount = result.filter(o => o.resultType === ResultTypes.SUCCESS).length;
|
|
500
624
|
const successMsg = `${successCount} ${successCount === 1 ? 'documento di dettaglio archiviato' : 'documenti di dettaglio archiviati'} con successo`;
|
|
@@ -518,14 +642,17 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
518
642
|
const ae = new ArchiveEngineByID(SDK_Globals.tmSession);
|
|
519
643
|
ae.TID = detailTID;
|
|
520
644
|
ae.Metadata_ClearAll();
|
|
521
|
-
// Valori del form + associazioni con il master (le associazioni prevalgono)
|
|
645
|
+
// Valori del form + associazioni con il master (le associazioni prevalgono). I metadati di
|
|
646
|
+
// sistema (TID, DID, FILEEXT...) non si archiviano: li assegna il sistema e mandarli fa fallire
|
|
647
|
+
// l'archiviazione. Il form li esclude già (vedi useArchiveListForm), qui è la rete di sicurezza
|
|
648
|
+
// per le righe che arrivano da altre strade, come la copia di un dettaglio già archiviato
|
|
522
649
|
const valueByMid = new Map();
|
|
523
650
|
for (const v of row.__values) {
|
|
524
|
-
if (v.mid !== undefined && v.value)
|
|
651
|
+
if (v.mid !== undefined && v.mid > 99 && v.value)
|
|
525
652
|
valueByMid.set(v.mid, v.value);
|
|
526
653
|
}
|
|
527
654
|
for (const a of associationMids) {
|
|
528
|
-
if (a.value)
|
|
655
|
+
if (a.mid > 99 && a.value)
|
|
529
656
|
valueByMid.set(a.mid, a.value);
|
|
530
657
|
}
|
|
531
658
|
valueByMid.forEach((value, mid) => {
|
|
@@ -579,7 +706,7 @@ export const useMultiMasterDetailDcmts = ({ dtd, masterDcmt, onClose }) => {
|
|
|
579
706
|
detailColumns, detailSupportsFile, archiveRows, currentRows,
|
|
580
707
|
focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys,
|
|
581
708
|
isOpenAddForm, editingRowId, editingRow, formInputMids,
|
|
582
|
-
openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues,
|
|
709
|
+
openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, copyArchivedDetailToRows,
|
|
583
710
|
// Archiviazione
|
|
584
711
|
stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync,
|
|
585
712
|
waitPanel, abortController: abortControllerRef.current,
|