@topconsultnpm/sdkui-react 6.22.0-dev2.3 → 6.22.0-dev2.30
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/TMButton.d.ts +13 -0
- package/lib/components/base/TMButton.js +137 -5
- package/lib/components/base/TMDataGrid.d.ts +2 -0
- package/lib/components/base/TMDataGrid.js +81 -8
- package/lib/components/base/TMEditorBase.d.ts +2 -0
- package/lib/components/base/TMFileManager.js +5 -3
- package/lib/components/base/TMFileManagerUtils.d.ts +1 -0
- package/lib/components/base/TMFileManagerUtils.js +16 -0
- package/lib/components/base/TMModal.d.ts +4 -0
- package/lib/components/base/TMModal.js +42 -11
- package/lib/components/base/TMPanel.d.ts +1 -0
- package/lib/components/base/TMPanel.js +79 -20
- package/lib/components/base/TMPopUp.js +3 -0
- package/lib/components/base/TMTooltip.d.ts +2 -1
- package/lib/components/base/TMTooltip.js +23 -6
- package/lib/components/base/TMTreeView.js +108 -67
- package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
- package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
- package/lib/components/choosers/TMDistinctValues.js +149 -142
- package/lib/components/choosers/TMMetadataChooser.js +1 -1
- package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
- package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
- package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
- package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
- package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
- package/lib/components/editors/TMDropDown.d.ts +3 -0
- package/lib/components/editors/TMDropDown.js +197 -5
- package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
- package/lib/components/editors/TMMetadataEditor.js +8 -3
- package/lib/components/editors/TMTextBox.js +2 -2
- package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
- package/lib/components/features/documents/TMDcmtBlog.d.ts +3 -0
- package/lib/components/features/documents/TMDcmtBlog.js +4 -4
- package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
- package/lib/components/features/documents/TMDcmtForm.js +112 -45
- package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
- package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
- package/lib/components/features/documents/TMDcmtPreview.js +10 -6
- package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
- package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
- package/lib/components/features/documents/TMMasterInfoFields.js +186 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +296 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +363 -0
- package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +259 -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/TMSearch.js +29 -16
- package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
- package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
- package/lib/components/features/search/TMSearchQueryPanel.js +28 -7
- package/lib/components/features/search/TMSearchResult.d.ts +9 -0
- package/lib/components/features/search/TMSearchResult.js +419 -108
- package/lib/components/features/search/TMTreeSelector.js +18 -5
- package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
- package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
- package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
- package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
- package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
- package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
- package/lib/components/layout/panelManager/types.d.ts +3 -0
- package/lib/components/layout/panelManager/utils.js +13 -11
- package/lib/components/viewers/TMMidViewer.js +4 -1
- package/lib/components/viewers/TMTidViewer.js +25 -11
- package/lib/helper/SDKUI_Globals.d.ts +82 -0
- package/lib/helper/SDKUI_Globals.js +119 -0
- package/lib/helper/SDKUI_Localizator.d.ts +24 -0
- package/lib/helper/SDKUI_Localizator.js +240 -0
- package/lib/helper/TMIcons.d.ts +1 -0
- package/lib/helper/TMIcons.js +3 -0
- package/lib/helper/dataGridSearchHelper.d.ts +28 -0
- package/lib/helper/dataGridSearchHelper.js +51 -0
- package/lib/helper/dcmtsHelper.d.ts +20 -0
- package/lib/helper/dcmtsHelper.js +58 -1
- package/lib/helper/helpers.d.ts +20 -0
- package/lib/helper/helpers.js +55 -1
- package/lib/helper/index.d.ts +1 -0
- package/lib/helper/index.js +1 -0
- package/lib/helper/queryHelper.d.ts +2 -0
- package/lib/helper/queryHelper.js +3 -1
- package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
- package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
- package/lib/hooks/useArchiveListForm.d.ts +39 -0
- package/lib/hooks/useArchiveListForm.js +46 -0
- package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
- package/lib/hooks/useCaseFlowApprove.js +157 -0
- package/lib/hooks/useDataUserIdItem.d.ts +1 -0
- package/lib/hooks/useDataUserIdItem.js +1 -0
- package/lib/hooks/useDocumentOperations.d.ts +2 -0
- package/lib/hooks/useDocumentOperations.js +28 -5
- package/lib/hooks/useMultiMasterDetailDcmts.d.ts +100 -0
- package/lib/hooks/useMultiMasterDetailDcmts.js +587 -0
- package/lib/hooks/useRelatedDocuments.js +2 -26
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
- package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
- package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
- package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
- package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
- package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
- package/lib/hooks/useTMDistinctValuesSource.js +105 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
- package/lib/services/caseflow/CaseFlowService.js +693 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
- package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
- package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
- package/lib/services/caseflow/caseFlowNameCache.js +116 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
- package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
- package/lib/services/index.d.ts +4 -0
- package/lib/services/index.js +4 -0
- package/lib/services/platform_services.d.ts +1 -1
- package/package.json +66 -66
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
4
|
import { LoadIndicator } from 'devextreme-react';
|
|
5
5
|
import { AppModules, DataColumnTypes, DataListViewModes, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataDomains, SDK_Globals, SystemMIDsAsNumber, UserListCacheService, } from '@topconsultnpm/sdk-ts';
|
|
6
|
-
import { deepCompare, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat, generateUniqueColumnKeys, genUniqueId, getColumnFormatInfo, getSearchToolbarVisibility, IconAll, IconBoard, IconDcmtTypeSys, IconDelete, IconMenuVertical, IconPlatform, IconRefresh, IconSearchCheck, IconShow, isApprovalWorkflowView, isSign4TopEnabled, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, SDKUI_Globals, SDKUI_Localizator } from '../../../helper';
|
|
6
|
+
import { customizeSearchByDisplayText, deepCompare, formatDateTimeByMetadataFormat, formatNumberByMetadataFormat, generateUniqueColumnKeys, genUniqueId, getColumnFormatInfo, getDateDisplayText, getNumberDisplayText, getSearchToolbarVisibility, IconAll, IconBoard, IconDcmtTypeSys, IconDelete, IconDuplicate, IconEdit, IconMenuVertical, IconPlatform, IconRefresh, IconSearchCheck, IconShow, isApprovalWorkflowView, isSign4TopEnabled, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, SDKUI_Globals, SDKUI_Localizator, IconCloseOutline, getSearchResultPanelLayout, getSearchResultPanelLayoutSignature, hasSearchResultPanelLayout, saveSearchResultPanelLayout, subscribeSearchResultPanelLayout } from '../../../helper';
|
|
7
7
|
import { CounterItemKey } from '../../base/TMCounterContainer';
|
|
8
8
|
import { getDcmtCicoStatus } from '../../../helper/checkinCheckoutManager';
|
|
9
9
|
import { DcmtOperationTypes, SearchResultContext, } from '../../../ts';
|
|
10
|
-
import { Gutters } from '../../../utils/theme';
|
|
10
|
+
import { Gutters, TMColors } from '../../../utils/theme';
|
|
11
11
|
import { useDataListItem } from '../../../hooks/useDataListItem';
|
|
12
12
|
import { useDataUserIdItem } from '../../../hooks/useDataUserIdItem';
|
|
13
13
|
import { getSelectedDcmtsOrFocused, useDocumentOperations } from '../../../hooks/useDocumentOperations';
|
|
@@ -23,7 +23,8 @@ import { StyledMultiViewPanel } from '../../base/Styled';
|
|
|
23
23
|
import { TMLayoutWaitingContainer } from '../../base/TMWaitPanel';
|
|
24
24
|
import TMMetadataValues from '../../editors/TMMetadataValues';
|
|
25
25
|
import TMTidViewer from '../../viewers/TMTidViewer';
|
|
26
|
-
import {
|
|
26
|
+
import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
|
|
27
|
+
import { TMPanelManagerWithPersistenceProvider } from '../../layout/panelManager/TMPanelManagerWithPersistenceProvider';
|
|
27
28
|
import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
|
|
28
29
|
import TMContextMenu from '../../NewComponents/ContextMenu/TMContextMenu';
|
|
29
30
|
import TMToppyDraggableHelpCenter from '../assistant/TMToppyDraggableHelpCenter';
|
|
@@ -51,13 +52,25 @@ const orderByName = (array) => {
|
|
|
51
52
|
return 1;
|
|
52
53
|
} return 0; });
|
|
53
54
|
};
|
|
55
|
+
//#endregion Helper Methods
|
|
56
|
+
/**
|
|
57
|
+
* Contesti che ricordano il layout dei pannelli, condividendone uno solo.
|
|
58
|
+
* Gli altri sono esclusi di proposito: FREE_SEARCH ha un pannello in più, quindi il suo layout non vale
|
|
59
|
+
* per gli altri, e i restanti mostrano il risultato dentro contenitori con esigenze di spazio loro.
|
|
60
|
+
*/
|
|
61
|
+
const PERSISTED_LAYOUT_CONTEXTS = [
|
|
62
|
+
SearchResultContext.METADATA_SEARCH,
|
|
63
|
+
SearchResultContext.WORKFLOW_APPROVE,
|
|
64
|
+
];
|
|
54
65
|
const TMSearchResult = ({
|
|
55
66
|
// Data
|
|
56
67
|
groupId, searchResults = [], context = SearchResultContext.METADATA_SEARCH, title, selectedSearchResultTID, floatingActionConfig, workingGroupContext = undefined, inputDID,
|
|
57
68
|
// Boolean flags to enable/disable features
|
|
58
|
-
autoFocusFirstRow = true, formAutoOpen, isVisible = true, allowRelations = true, openDcmtFormAsModal = false, showSearchResultSidebar = true, showDcmtFormSidebar = true, showSelector = false, isClosable = false, allowFloatingBar = true, showToolbarHeader = true, showBackButton = true, disableAccordionIfSingleCategory = false, editPdfForm = false, openS4TViewer = false, showTodoDcmtForm = false, showToppyDraggableHelpCenter = true, toppyHelpCenterUsePortal = false, showNoDcmtFoundMessage = true, enablePinIcons = true,
|
|
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,
|
|
70
|
+
// Counters
|
|
71
|
+
counterConfig,
|
|
59
72
|
// Callbacks (optional)
|
|
60
|
-
openInOffice, onRefreshAfterAddDcmtToFavs, onRefreshSearchAsyncDatagrid, onSelectedTIDChanged, onWFOperationCompleted, onClose, onFileOpened, onTaskCreateRequest, openWGsCopyMoveForm, openCommentFormCallback, openAddDocumentForm, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, passToArchiveCallback, onReferenceClick,
|
|
73
|
+
openInOffice, onRefreshAfterAddDcmtToFavs, onRefreshSearchAsyncDatagrid, onSelectedTIDChanged, onSelectedTIDCleared, onWFOperationCompleted, onClose, onFileOpened, onTaskCreateRequest, openWGsCopyMoveForm, openCommentFormCallback, openAddDocumentForm, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, passToArchiveCallback, onReferenceClick,
|
|
61
74
|
// Tasks
|
|
62
75
|
allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback,
|
|
63
76
|
// Navigation
|
|
@@ -69,13 +82,19 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
69
82
|
// Quando l'utente seleziona manualmente, il ref viene settato a false e il focus automatico
|
|
70
83
|
// torna al comportamento normale.
|
|
71
84
|
const isFirstAutoFocusRef = useRef(true);
|
|
85
|
+
// Mostra il pannello lista. È una ref perché il TMPanelManagerProvider sta dentro al JSX di
|
|
86
|
+
// questo componente, quindi qui il context non si può usare: la riempie ShowMainPanelBridge.
|
|
87
|
+
const showMainPanelRef = useRef(null);
|
|
72
88
|
// ID state to force remount of components
|
|
73
89
|
const [id, setID] = useState('');
|
|
74
90
|
// Document type descriptor of the currently document
|
|
75
91
|
const [fromDTD, setFromDTD] = useState();
|
|
76
92
|
const [currentDcmt, setCurrentDcmt] = useState();
|
|
77
|
-
//
|
|
78
|
-
|
|
93
|
+
// Passo corrente. Su mobile decide anche quale pannello è a schermo (vedi isSteppedLayout).
|
|
94
|
+
// 'auto' -> l'utente non ha ancora scelto: si può selezionare il primo tipo documento
|
|
95
|
+
// 'selector' -> è tornato indietro col back: non selezionare niente al posto suo
|
|
96
|
+
// 'results' -> è sulla griglia del tipo scelto
|
|
97
|
+
const [navStep, setNavStep] = useState('auto');
|
|
79
98
|
// State to control the selected search result
|
|
80
99
|
const [selectedSearchResult, setSelectedSearchResult] = useState();
|
|
81
100
|
// State to control the search results to show in the datagrid
|
|
@@ -303,7 +322,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
303
322
|
createTaskFromDocumentOrWorkItem();
|
|
304
323
|
}
|
|
305
324
|
};
|
|
306
|
-
const { operationItems, renderFloatingBar, renderDcmtOperations, features, } = useDocumentOperations({
|
|
325
|
+
const { operationItems, selectedDcmtInfos, renderFloatingBar, renderDcmtOperations, features, } = useDocumentOperations({
|
|
307
326
|
context: context,
|
|
308
327
|
documentData: {
|
|
309
328
|
dtd: fromDTD,
|
|
@@ -377,9 +396,60 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
377
396
|
openTaskFormHandler,
|
|
378
397
|
},
|
|
379
398
|
});
|
|
380
|
-
const { isOpenDcmtForm, openFormHandler, dcmtFormLayoutMode, onDcmtFormOpenChange, showSearchTMDatagrid,
|
|
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;
|
|
400
|
+
// ----- Azioni della colonna di riga (showActionsColumn) -----------------------------------------
|
|
401
|
+
// Le voci di operationItems agiscono sui documenti selezionati o, in mancanza, su quello a fuoco:
|
|
402
|
+
// non accettano una riga come argomento. Il click sull'icona quindi azzera la selezione e porta il
|
|
403
|
+
// fuoco sulla riga cliccata, poi attende. L'attesa non è sul fuoco ma su selectedDcmtInfos, che
|
|
404
|
+
// l'hook ricalcola in un effect (un render più tardi): lanciare l'operazione prima significherebbe
|
|
405
|
+
// eseguirla sulla riga precedente.
|
|
406
|
+
const pendingRowActionRef = useRef(undefined);
|
|
407
|
+
const requestRowAction = useCallback((operationId, rowData) => {
|
|
408
|
+
if (!showActionsColumn)
|
|
409
|
+
return;
|
|
410
|
+
if (rowData?.TID === undefined || rowData?.DID === undefined) {
|
|
411
|
+
ShowAlert({ message: SDKUI_Localizator.InvalidDcmt, mode: "warning", duration: 3000 });
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
pendingRowActionRef.current = { operationId, TID: rowData.TID, DID: rowData.DID };
|
|
415
|
+
isFirstAutoFocusRef.current = false;
|
|
416
|
+
setSelectedItems([]);
|
|
417
|
+
setFocusedItem(rowData);
|
|
418
|
+
}, [showActionsColumn]);
|
|
419
|
+
// La richiesta viene scartata se il documento sparisce dai risultati (es. refresh dopo
|
|
420
|
+
// un'eliminazione): senza questo resterebbe appesa e partirebbe al prossimo cambio di fuoco.
|
|
421
|
+
useEffect(() => { pendingRowActionRef.current = undefined; }, [selectedSearchResult, lastUpdateSearchTime]);
|
|
422
|
+
useEffect(() => {
|
|
423
|
+
if (!showActionsColumn)
|
|
424
|
+
return;
|
|
425
|
+
const pending = pendingRowActionRef.current;
|
|
426
|
+
if (!pending)
|
|
427
|
+
return;
|
|
428
|
+
// L'hook non ha ancora recepito la riga richiesta: si riproverà al prossimo aggiornamento
|
|
429
|
+
if (selectedDcmtInfos.length !== 1)
|
|
430
|
+
return;
|
|
431
|
+
if (selectedDcmtInfos[0].TID !== pending.TID || selectedDcmtInfos[0].DID !== pending.DID)
|
|
432
|
+
return;
|
|
433
|
+
pendingRowActionRef.current = undefined;
|
|
434
|
+
const operation = findOperationItemById(operationItems, pending.operationId);
|
|
435
|
+
// Ora che il fuoco è sulla riga, `disabled` riflette i permessi per quel documento
|
|
436
|
+
if (!operation || operation.disabled || operation.visible === false)
|
|
437
|
+
return;
|
|
438
|
+
operation.onClick?.();
|
|
439
|
+
}, [showActionsColumn, selectedDcmtInfos, operationItems]);
|
|
381
440
|
const deviceType = useDeviceType();
|
|
382
441
|
const isMobile = deviceType === DeviceType.MOBILE;
|
|
442
|
+
const hasSingleDcmtType = currentSearchResults.length === 1;
|
|
443
|
+
// Su mobile si vede un pannello per volta; su desktop selettore e griglia sono affiancati
|
|
444
|
+
const isSteppedLayout = showSelector && isMobile;
|
|
445
|
+
// Un solo tipo documento su desktop: non c'è niente da scegliere, quindi il selettore sparisce
|
|
446
|
+
// (larghezza 0, nessun separatore) e lo spazio va tutto alla griglia. Il menu "Altro" permette
|
|
447
|
+
// di riaprirlo (vedi canToggleDcmtTypesSelection nell'hook).
|
|
448
|
+
const isSelectorCollapsed = showSelector && !isSteppedLayout && hasSingleDcmtType && !showDcmtTypesSelection;
|
|
449
|
+
// Senza selettore lo spazio va tutto alla griglia; altrimenti a step su mobile, affiancati su desktop
|
|
450
|
+
const splitterSizes = (!showSelector || isSelectorCollapsed) ? ['0%', '100%']
|
|
451
|
+
: isSteppedLayout ? (navStep === 'results' ? ['0', '100%'] : ['100%', '0'])
|
|
452
|
+
: ['30%', '70%'];
|
|
383
453
|
const selectedDocs = getSelectedDcmtsOrFocused(selectedItems, focusedItem);
|
|
384
454
|
// Disable the "Sign/Approve" button if:
|
|
385
455
|
// 1. No document or multiple documents are selected, OR
|
|
@@ -400,8 +470,14 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
400
470
|
setSelectedSearchResult(undefined);
|
|
401
471
|
return;
|
|
402
472
|
}
|
|
473
|
+
// Un solo tipo documento: selezionalo e vai ai risultati, a meno che l'utente sia tornato indietro
|
|
403
474
|
if (searchResults.length === 1) {
|
|
475
|
+
if (navStep === 'selector') {
|
|
476
|
+
setSelectedSearchResult(undefined);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
404
479
|
setSelectedSearchResult(searchResults[0]);
|
|
480
|
+
setNavStep('results');
|
|
405
481
|
return;
|
|
406
482
|
}
|
|
407
483
|
// Se il TID precedentemente selezionato esiste ancora nei nuovi risultati, mantienilo.
|
|
@@ -415,6 +491,11 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
415
491
|
return;
|
|
416
492
|
}
|
|
417
493
|
}
|
|
494
|
+
// L'utente è sul selettore
|
|
495
|
+
if (isMobile && navStep !== 'results') {
|
|
496
|
+
setSelectedSearchResult(undefined);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
418
499
|
// Altrimenti seleziona il primo risultato ordinato
|
|
419
500
|
setSelectedSearchResult(orderByName(searchResults)[0]);
|
|
420
501
|
}, [searchResults]);
|
|
@@ -471,7 +552,9 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
471
552
|
return;
|
|
472
553
|
if (focusedItem?.DID != inputDID)
|
|
473
554
|
return;
|
|
474
|
-
|
|
555
|
+
if (focusedItem?.TID === undefined)
|
|
556
|
+
return;
|
|
557
|
+
onDcmtFormOpenChange(true, LayoutModes.Update);
|
|
475
558
|
}, [inputDID, formAutoOpen, focusedItem]);
|
|
476
559
|
// Quando openS4TViewer è true, apri automaticamente il viewer S4T
|
|
477
560
|
useEffect(() => {
|
|
@@ -531,47 +614,75 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
531
614
|
let counters = (showSelector && disableAccordionIfSingleCategory && searchResults.length > 1) ? getSearchResultCountersSingleCategory(searchResults) : "";
|
|
532
615
|
if (title)
|
|
533
616
|
return title + counters;
|
|
534
|
-
|
|
617
|
+
// Nome del tipo documento selezionato, accodato al titolo
|
|
618
|
+
const selectedDcmtTypeName = selectedSearchResult?.fromName ?? (searchResults?.length === 1 ? searchResults[0]?.fromName : undefined);
|
|
619
|
+
const withSelectedDcmtTypeName = (baseTitle) => selectedDcmtTypeName ? `${baseTitle} - ${selectedDcmtTypeName}` : baseTitle;
|
|
620
|
+
let titleHeader = withSelectedDcmtTypeName('Ricerca per metadati');
|
|
535
621
|
switch (context) {
|
|
536
622
|
case SearchResultContext.MASTER_DETAIL:
|
|
537
623
|
case SearchResultContext.METADATA_SEARCH:
|
|
538
624
|
titleHeader = `${searchResults?.length > 1 ? selectedSearchResult?.fromName ?? 'Ricerca per metadati' : searchResults[0]?.fromName ?? 'Ricerca per metadati'}`;
|
|
539
625
|
break;
|
|
540
626
|
case SearchResultContext.FAVORITES_AND_RECENTS:
|
|
541
|
-
titleHeader = SDKUI_Localizator.FavoritesAndRecentDcmts;
|
|
627
|
+
titleHeader = withSelectedDcmtTypeName(SDKUI_Localizator.FavoritesAndRecentDcmts);
|
|
542
628
|
break;
|
|
543
629
|
case SearchResultContext.WORKFLOW_APPROVE:
|
|
544
|
-
titleHeader = SDKUI_Localizator.WorkflowApproval;
|
|
630
|
+
titleHeader = withSelectedDcmtTypeName(SDKUI_Localizator.WorkflowApproval);
|
|
545
631
|
break;
|
|
546
632
|
case SearchResultContext.FREE_SEARCH:
|
|
547
|
-
titleHeader =
|
|
633
|
+
titleHeader = withSelectedDcmtTypeName('Risultato della ricerca full text');
|
|
548
634
|
break;
|
|
549
635
|
}
|
|
550
636
|
return titleHeader + counters;
|
|
551
637
|
};
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
setSelectedSearchResult(undefined);
|
|
570
|
-
}
|
|
571
|
-
setSplitterSize(['100%', '0']);
|
|
572
|
-
onClose?.();
|
|
638
|
+
// Dai risultati si risale al selettore dei tipi documento
|
|
639
|
+
const canGoBackToSelector = isSteppedLayout && navStep === 'results' && selectedSearchResult !== undefined;
|
|
640
|
+
/**
|
|
641
|
+
* Back di tutti i pannelli (e X di chiusura se isClosable). Su mobile risale un livello alla volta:
|
|
642
|
+
* pannello secondario -> lista dei risultati -> selettore dei tipi documento -> uscita.
|
|
643
|
+
* Su desktop i pannelli sono affiancati, quindi resta solo l'uscita.
|
|
644
|
+
*
|
|
645
|
+
* @param fromSecondaryPanel - true se il back arriva da un pannello secondario
|
|
646
|
+
*/
|
|
647
|
+
const onBack = useCallback((fromSecondaryPanel = false) => {
|
|
648
|
+
// Non è previsto nessun modo di uscire: non fare niente
|
|
649
|
+
if (!showBackButton && !isClosable)
|
|
650
|
+
return;
|
|
651
|
+
// Torna alla lista senza toccare gli stati: l'utente deve ritrovare la sua selezione
|
|
652
|
+
if (fromSecondaryPanel) {
|
|
653
|
+
showMainPanelRef.current?.();
|
|
654
|
+
return;
|
|
573
655
|
}
|
|
574
|
-
|
|
656
|
+
// Sia il ritorno al selettore sia l'uscita ripartono da una lista pulita
|
|
657
|
+
setSelectedSearchResult(undefined);
|
|
658
|
+
setFocusedItem(undefined);
|
|
659
|
+
setSelectedItems([]);
|
|
660
|
+
setSelectedRowKeysFromDataGrid([]);
|
|
661
|
+
setCurrentDcmt(undefined);
|
|
662
|
+
setCurrentMetadataValues([]);
|
|
663
|
+
setShowIndexingInfo(false);
|
|
664
|
+
isFirstAutoFocusRef.current = true;
|
|
665
|
+
// Il chiamante può tenere allineato il suo TID selezionato (es. selectedSearchResultTID)
|
|
666
|
+
onSelectedTIDCleared?.();
|
|
667
|
+
// [2] l'utente ha chiesto la lista: resta lì finché non sceglie lui, niente auto-selezione.
|
|
668
|
+
// [1] si esce: si riparte da capo, quindi l'auto-selezione torna permessa.
|
|
669
|
+
setNavStep(canGoBackToSelector ? 'selector' : 'auto');
|
|
670
|
+
// [2] C'è ancora un livello sopra: restiamo dentro, sul selettore
|
|
671
|
+
if (canGoBackToSelector)
|
|
672
|
+
return;
|
|
673
|
+
// [1] Si esce e si avvisa il chiamante
|
|
674
|
+
onClose?.();
|
|
675
|
+
}, [canGoBackToSelector, showBackButton, isClosable, onClose, onSelectedTIDCleared]);
|
|
676
|
+
/**
|
|
677
|
+
* Back del pannello lista. `undefined` = freccia non renderizzata, quindi la mostriamo solo se
|
|
678
|
+
* ha dove tornare: uno step intermedio, o un onClose per uscire. Con `isClosable` basta la X.
|
|
679
|
+
*/
|
|
680
|
+
const backHandler = (showBackButton && !isClosable && (canGoBackToSelector || onClose !== undefined)) ? onBack : undefined;
|
|
681
|
+
/**
|
|
682
|
+
* Back dei pannelli secondari: la stessa onBack marcata come livello [3]. Solo su mobile:
|
|
683
|
+
* su desktop stanno affiancati alla lista e non c'è niente da cui tornare.
|
|
684
|
+
*/
|
|
685
|
+
const backHandlerSecondary = useMemo(() => (isMobile && backHandler) ? () => backHandler(true) : undefined, [isMobile, backHandler]);
|
|
575
686
|
const refreshDataGridAfterRemoveAsync = async () => {
|
|
576
687
|
let index = selectedSearchResult?.dtdResult?.columns?.findIndex(col => col.caption === 'DID');
|
|
577
688
|
let selectedRows = [];
|
|
@@ -603,21 +714,6 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
603
714
|
return newResults;
|
|
604
715
|
});
|
|
605
716
|
};
|
|
606
|
-
/**
|
|
607
|
-
* Restituisce l'handler onBack per i pannelli, evitando duplicazione di codice.
|
|
608
|
-
* @param isMainPanel - Se true, è il pannello principale dei risultati di ricerca
|
|
609
|
-
*/
|
|
610
|
-
const getOnBackHandler = (isMainPanel = false) => {
|
|
611
|
-
// Se showBackButton è false, non mostrare mai il back
|
|
612
|
-
if (showBackButton === false)
|
|
613
|
-
return undefined;
|
|
614
|
-
// Mobile: mostra back su tutti i pannelli (se showBackButton è definito e non è closable)
|
|
615
|
-
if (isMobile) {
|
|
616
|
-
return (showBackButton !== undefined && !isClosable) ? onBack : undefined;
|
|
617
|
-
}
|
|
618
|
-
// Desktop: mostra back solo sul pannello principale dei risultati (se showBackButton è definito e non è closable)
|
|
619
|
-
return (showBackButton !== undefined && isMainPanel && !isClosable) ? onBack : undefined;
|
|
620
|
-
};
|
|
621
717
|
const removeDcmtFromFavsOrRecents = async () => {
|
|
622
718
|
switch (selectedSearchResult?.category) {
|
|
623
719
|
case "Favorites":
|
|
@@ -634,9 +730,9 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
634
730
|
};
|
|
635
731
|
const onSearchResultSelectionChanged = (e) => {
|
|
636
732
|
setSelectedSearchResult(e);
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
733
|
+
// Scelto il tipo documento si passa ai risultati (a livello di layout è ignorato se i due
|
|
734
|
+
// step sono affiancati, ma vale comunque come "l'utente non è più fermo sul selettore")
|
|
735
|
+
setNavStep('results');
|
|
640
736
|
onSelectedTIDChanged?.(e.fromTID);
|
|
641
737
|
};
|
|
642
738
|
const showToppyForApprove = useMemo(() => {
|
|
@@ -660,14 +756,15 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
660
756
|
&& !showTodoDcmtForm);
|
|
661
757
|
}, [showToppyForApprove, showToppyDraggableHelpCenter, selectedDocs, showApprovePopup, showRejectPopup, showReAssignPopup, showMoreInfoPopup, editPdfForm, openS4TViewer, showTodoDcmtForm]);
|
|
662
758
|
const searchResutlToolbar = _jsxs(_Fragment, { children: [context === SearchResultContext.FAVORITES_AND_RECENTS &&
|
|
663
|
-
_jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: onRefreshSearchAsyncDatagrid }), _jsx(TMContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) })
|
|
759
|
+
_jsx("div", { style: { display: 'flex', alignItems: 'center', gap: '5px' }, children: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconDelete, { color: 'white' }), caption: "Rimuovi da " + (selectedSearchResult?.category === "Favorites" ? '"Preferiti"' : '"Recenti"'), disabled: getSelectedDcmtsOrFocused(selectedItems, focusedItem).length <= 0, onClick: removeDcmtFromFavsOrRecents }) }), _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconRefresh, { color: 'white' }), caption: SDKUI_Localizator.Refresh, onClick: onRefreshSearchAsyncDatagrid }), _jsx(TMContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) }), showToolbarCloseButton && onClose &&
|
|
760
|
+
_jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconCloseOutline, { color: 'white' }), caption: SDKUI_Localizator.Close, onClick: onClose })] });
|
|
664
761
|
const tmSearchResult = useMemo(() => (!searchResults || searchResults.length <= 0)
|
|
665
762
|
? _jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%' }, children: [_jsx(IconBoard, { fontSize: 96 }), showNoDcmtFoundMessage && _jsx("div", { style: { fontSize: "15px", marginTop: "10px" }, children: SDKUI_Localizator.NoDcmtFound }), openAddDocumentForm && _jsx("div", { style: { marginTop: "10px" }, children: _jsx(TMButton, { fontSize: "15px", icon: _jsx("i", { className: 'dx-icon-share' }), caption: SDKUI_Localizator.Share, onClick: openAddDocumentForm }) })] })
|
|
666
763
|
:
|
|
667
|
-
_jsxs(_Fragment, { children: [_jsxs(TMLayoutItem, { height: '100%', children: [_jsxs("div", { ref: floatingBarContainerRef, style: { position: 'relative', height: '100%', width: '100%' }, children: [_jsxs(TMSplitterLayout, { direction: 'horizontal', overflow: 'visible', separatorSize: Gutters.getGutters(), separatorActiveColor: 'transparent', separatorColor: 'transparent', min: ['0', '0'], showSeparator: showSelector &&
|
|
668
|
-
_jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile ||
|
|
764
|
+
_jsxs(_Fragment, { children: [_jsxs(TMLayoutItem, { height: '100%', children: [_jsxs("div", { ref: floatingBarContainerRef, style: { position: 'relative', height: '100%', width: '100%' }, children: [_jsxs(TMSplitterLayout, { direction: 'horizontal', overflow: 'visible', separatorSize: Gutters.getGutters(), separatorActiveColor: 'transparent', separatorColor: 'transparent', min: ['0', '0'], showSeparator: showSelector && !isSteppedLayout && !isSelectorCollapsed, start: splitterSizes, children: [showSelector ?
|
|
765
|
+
_jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, disableAccordionIfSingleCategory: disableAccordionIfSingleCategory, selectedTID: selectedSearchResultTID, selectedSearchResult: selectedSearchResult, autoSelectFirst: !isMobile || hasSingleDcmtType, onSelectionChanged: onSearchResultSelectionChanged }) })
|
|
669
766
|
:
|
|
670
|
-
_jsx(_Fragment, {}), _jsx(TMLayoutItem, { children: _jsx(TMSearchResultGrid, { openInOffice: openInOffice, fromDTD: fromDTD, operationItems: operationItems, allUsers: allUsers, inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, showExportForm: showExportForm, onFocusedItemChanged: handleFocusedItemChangedFromGrid, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync({ inputDcmts, downloadType, downloadMode, onFileDownloaded: onFileOpened, confirmAttachments }), lastUpdateSearchTime: lastUpdateSearchTime, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], onSelectionChanged: (items) => { setSelectedItems(items); }, onDblClick: () => openFormHandler(LayoutModes.Update), showSearchTMDatagrid: showSearchTMDatagrid, onVisibleItemChanged: setVisibleItems, updateDataColumnsFromDataGrid: updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid: updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid: updateSelectedRowKeysFromDataGrid, disableAutoFocus: autoFocusFirstRow === false && isFirstAutoFocusRef.current, dcmtsReturned: dcmtsReturned, dcmtsFound: dcmtsFound }) })] }), renderFloatingBar] }), _jsx(TMToppyDraggableHelpCenter, { usePortal: toppyHelpCenterUsePortal, isVisible: isToppyHelpCenterVisible, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { dtd: fromDTD, deviceType: deviceType, onApprove: () => {
|
|
767
|
+
_jsx(_Fragment, {}), _jsx(TMLayoutItem, { children: _jsx(TMSearchResultGrid, { openInOffice: openInOffice, fromDTD: fromDTD, operationItems: operationItems, allUsers: allUsers, inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, showExportForm: showExportForm, onFocusedItemChanged: handleFocusedItemChangedFromGrid, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync({ inputDcmts, downloadType, downloadMode, onFileDownloaded: onFileOpened, confirmAttachments }), lastUpdateSearchTime: lastUpdateSearchTime, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], onSelectionChanged: (items) => { setSelectedItems(items); }, onDblClick: () => openFormHandler(LayoutModes.Update), showSearchTMDatagrid: showSearchTMDatagrid, onVisibleItemChanged: setVisibleItems, updateDataColumnsFromDataGrid: updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid: updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid: updateSelectedRowKeysFromDataGrid, disableAutoFocus: autoFocusFirstRow === false && isFirstAutoFocusRef.current, dcmtsReturned: dcmtsReturned, dcmtsFound: dcmtsFound, showCounterConfig: showCounterConfig, counterConfig: counterConfig, showActionsColumn: showActionsColumn, compactPager: compactPager, onRowActionRequest: requestRowAction }) })] }), renderFloatingBar] }), _jsx(TMToppyDraggableHelpCenter, { usePortal: toppyHelpCenterUsePortal, isVisible: isToppyHelpCenterVisible, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { dtd: fromDTD, deviceType: deviceType, onApprove: () => {
|
|
671
768
|
updateShowApprovePopup(true);
|
|
672
769
|
}, onSignApprove: () => {
|
|
673
770
|
handleSignApprove();
|
|
@@ -687,7 +784,6 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
687
784
|
showApprovePopup,
|
|
688
785
|
showRejectPopup,
|
|
689
786
|
showReAssignPopup,
|
|
690
|
-
isOpenBatchUpdate,
|
|
691
787
|
isOpenDetails,
|
|
692
788
|
isOpenMaster,
|
|
693
789
|
isOpenDcmtForm,
|
|
@@ -698,6 +794,10 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
698
794
|
openS4TViewer,
|
|
699
795
|
showBackButton,
|
|
700
796
|
isMobile,
|
|
797
|
+
navStep,
|
|
798
|
+
isSteppedLayout,
|
|
799
|
+
isSelectorCollapsed,
|
|
800
|
+
currentSearchResults,
|
|
701
801
|
currentMetadataValues,
|
|
702
802
|
onRefreshSearchAsyncDatagrid,
|
|
703
803
|
handleNavigateToWGs,
|
|
@@ -705,9 +805,14 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
705
805
|
editPdfForm,
|
|
706
806
|
showNoDcmtFoundMessage
|
|
707
807
|
]);
|
|
708
|
-
|
|
808
|
+
/**
|
|
809
|
+
* Il risultato è davvero a video: le pagine restano montate anche in secondo piano, e senza questo
|
|
810
|
+
* bacheca e anteprima scaricherebbero il documento fuori campo.
|
|
811
|
+
*/
|
|
812
|
+
const isSearchResultOnScreen = isVisible && !isOpenDcmtForm && !isOpenDetails && !isOpenMaster;
|
|
813
|
+
const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: focusedItem?.TID, did: focusedItem?.DID, isPageVisible: isSearchResultOnScreen, fetchBlogDataTrigger: refreshBlogTrigger, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), [focusedItem, allTasks, refreshBlogTrigger, isSearchResultOnScreen, handleNavigateToWGs, handleNavigateToDossiers, onRefreshBlogDatagrid]);
|
|
709
814
|
const tmSysMetadata = useMemo(() => _jsx(TMMetadataValues, { layoutMode: LayoutModes.Update, openChooserBySingleClick: true, TID: focusedItem?.TID, isReadOnly: true, deviceType: deviceType, metadataValues: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: currentMetadataValues.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [] }), [focusedItem, currentMetadataValues, deviceType]);
|
|
710
|
-
const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { refreshPreviewTrigger: refreshPreviewTrigger, currentDcmt: currentDcmt, onBack:
|
|
815
|
+
const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { refreshPreviewTrigger: refreshPreviewTrigger, currentDcmt: currentDcmt, isPageVisible: isSearchResultOnScreen, onBack: backHandlerSecondary }), [currentDcmt, refreshPreviewTrigger, isSearchResultOnScreen, backHandlerSecondary]);
|
|
711
816
|
// Auto-fetch indexing info when drawer is open and focusedItem changes
|
|
712
817
|
useEffect(() => {
|
|
713
818
|
if (!focusedItem || !showIndexingInfo)
|
|
@@ -764,6 +869,22 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
764
869
|
}
|
|
765
870
|
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', overflow: 'hidden', width: '100%' }, children: [_jsx("div", { style: { padding: '10px', overflow: 'auto', flex: 1 }, children: _jsx("div", { dangerouslySetInnerHTML: { __html: ftExplanation } }) }), _jsxs(StyledIndexingInfoSection, { children: [_jsxs(StyledIndexingToggle, { onClick: handleToggleIndexingInfo, disabled: loadingIndexingInfo, children: [_jsx(StyledLeftContent, { children: _jsx("span", { children: SDKUI_Localizator.IndexingInformation }) }), _jsx(StyledRightContent, { children: _jsx(StyledChevron, { "$isOpen": showIndexingInfo, children: "\u25BC" }) })] }), showIndexingInfo && indexingInfo && (_jsxs(StyledIndexingInfoBox, { children: [_jsx("div", { dangerouslySetInnerHTML: { __html: indexingInfo } }), loadingIndexingInfo && (_jsxs("div", { style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', background: 'rgba(255, 255, 255, 0.9)', padding: '10px', borderRadius: '4px', boxShadow: '0 2px 8px rgba(0,0,0,0.15)' }, children: [SDKUI_Localizator.Loading, "..."] }))] }))] })] }));
|
|
766
871
|
}, [selectedSearchResult, focusedItem, indexingInfo, showIndexingInfo, loadingIndexingInfo]);
|
|
872
|
+
/**
|
|
873
|
+
* Su mobile i pannelli si mostrano uno per volta, quindi il layout non va ricordato; con groupId
|
|
874
|
+
* i pannelli sono di un panel manager esterno, che ha un layout suo. Senza la barra laterale
|
|
875
|
+
* l'utente non può aprire/chiudere i pannelli: non ha senso applicargli il layout salvato (li
|
|
876
|
+
* troverebbe aperti senza poterli chiudere) né lasciargli sporcare quello condiviso.
|
|
877
|
+
* Il layout salvato vale solo per il login con Surfer: gli altri moduli hanno layout propri.
|
|
878
|
+
*/
|
|
879
|
+
const isLayoutPersisted = SDK_Globals.tmSession?.SessionDescr?.appModuleID === AppModules.SURFER && !isMobile && !groupId?.length && showSearchResultSidebar && PERSISTED_LAYOUT_CONTEXTS.includes(context);
|
|
880
|
+
/**
|
|
881
|
+
* Pannelli che questo risultato non può mostrare (la bacheca è l'unico, vedi PanelDisabledStateHandler).
|
|
882
|
+
* Il layout è condiviso con gli altri risultati aperti: qui non vanno aperti quando il layout lo chiede,
|
|
883
|
+
* e il loro stato salvato non va toccato, altrimenti li si chiuderebbe dove invece sono attivi.
|
|
884
|
+
*/
|
|
885
|
+
const disabledLayoutPanelIds = isBoardDisabled ? ['tmBlog'] : [];
|
|
886
|
+
/** Bacheca aperta nel layout salvato: da riaprire quando il tipo documento torna a prevederla */
|
|
887
|
+
const savedBlogVisible = isLayoutPersisted && (getSearchResultPanelLayout()['tmBlog']?.visible ?? false);
|
|
767
888
|
const allInitialPanelVisibility = {
|
|
768
889
|
'tmSearchResult': true,
|
|
769
890
|
'tmBlog': false,
|
|
@@ -786,9 +907,10 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
786
907
|
component: tmSearchResult,
|
|
787
908
|
panelContainer: {
|
|
788
909
|
title: getTitleHeader(),
|
|
910
|
+
enableTitleMarquee: true,
|
|
789
911
|
showHeader: showToolbarHeader,
|
|
790
912
|
allowMaximize: !isMobile,
|
|
791
|
-
onBack:
|
|
913
|
+
onBack: backHandler,
|
|
792
914
|
onClose: isClosable ? onBack : undefined,
|
|
793
915
|
toolbar: searchResutlToolbar
|
|
794
916
|
},
|
|
@@ -803,7 +925,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
803
925
|
panelContainer: {
|
|
804
926
|
title: SDKUI_Localizator.BlogCase,
|
|
805
927
|
allowMaximize: !isMobile,
|
|
806
|
-
onBack:
|
|
928
|
+
onBack: backHandlerSecondary,
|
|
807
929
|
}
|
|
808
930
|
},
|
|
809
931
|
toolbarOptions: { icon: _jsx(IconBoard, { fontSize: 24 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmBlog, orderNumber: 2, isActive: allInitialPanelVisibility['tmBlog'] }
|
|
@@ -816,7 +938,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
816
938
|
panelContainer: {
|
|
817
939
|
title: SDKUI_Localizator.MetadataSystem,
|
|
818
940
|
allowMaximize: !isMobile,
|
|
819
|
-
onBack:
|
|
941
|
+
onBack: backHandlerSecondary,
|
|
820
942
|
}
|
|
821
943
|
},
|
|
822
944
|
toolbarOptions: { icon: _jsx(IconDcmtTypeSys, { fontSize: 24 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmSysMetadata, orderNumber: 3, isActive: allInitialPanelVisibility['tmSysMetadata'] }
|
|
@@ -830,7 +952,7 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
830
952
|
panelContainer: {
|
|
831
953
|
title: SDKUI_Localizator.ResultDetails,
|
|
832
954
|
allowMaximize: !isMobile,
|
|
833
|
-
onBack:
|
|
955
|
+
onBack: backHandlerSecondary,
|
|
834
956
|
}
|
|
835
957
|
},
|
|
836
958
|
toolbarOptions: { icon: _jsx(IconPlatform, { fontSize: 20 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmFullTextSearch, orderNumber: 4, isActive: allInitialPanelVisibility['tmFullTextSearch'] }
|
|
@@ -844,7 +966,9 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
844
966
|
},
|
|
845
967
|
toolbarOptions: { icon: _jsx(IconShow, { fontSize: 24 }), visible: getSearchToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmDcmtPreview, orderNumber: context === SearchResultContext.FREE_SEARCH ? 5 : 4, isActive: allInitialPanelVisibility['tmDcmtPreview'] }
|
|
846
968
|
}
|
|
847
|
-
], [tmSearchResult, tmBlog, tmSysMetadata, tmDcmtPreview, tmFullTextSearch, showToolbarHeader, context, isMobile]);
|
|
969
|
+
], [tmSearchResult, tmBlog, tmSysMetadata, tmDcmtPreview, tmFullTextSearch, showToolbarHeader, context, isMobile, backHandler, backHandlerSecondary, isClosable, onBack]);
|
|
970
|
+
/** Contenuto del panel manager: con groupId sta nel provider del contenitore, altrimenti in quello qui sotto */
|
|
971
|
+
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 })] }));
|
|
848
972
|
return (_jsxs(StyledMultiViewPanel, { "$isVisible": isVisible, children: [_jsx(StyledMultiViewPanel, { "$isVisible": !isOpenDcmtForm && !isOpenDetails && !isOpenMaster, style: {
|
|
849
973
|
display: 'flex',
|
|
850
974
|
flexDirection: isMobile ? 'column' : 'row',
|
|
@@ -852,13 +976,39 @@ handleNavigateToWGs, handleNavigateToDossiers, fetchRemoteCertificates, graphome
|
|
|
852
976
|
gap: Gutters.getGutters(),
|
|
853
977
|
width: '100%',
|
|
854
978
|
height: '100%',
|
|
855
|
-
}, children: _jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: _jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showCicoWaitPanel, showWaitPanelPrimary: showCicoPrimaryProgress, waitPanelTitle: cicoWaitPanelTitle, waitPanelTextPrimary: cicoPrimaryProgressText, waitPanelValuePrimary: cicoPrimaryProgressValue, waitPanelMaxValuePrimary: cicoPrimaryProgressMax, isCancelable: true, abortController: abortControllerLocal, children: (groupId && groupId.length > 0) ?
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
979
|
+
}, children: _jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: _jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showCicoWaitPanel, showWaitPanelPrimary: showCicoPrimaryProgress, waitPanelTitle: cicoWaitPanelTitle, waitPanelTextPrimary: cicoPrimaryProgressText, waitPanelValuePrimary: cicoPrimaryProgressValue, waitPanelMaxValuePrimary: cicoPrimaryProgressMax, isCancelable: true, abortController: abortControllerLocal, children: (groupId && groupId.length > 0) ? panelManagerContent
|
|
980
|
+
// Senza le prop di persistenza il provider si comporta come quello base:
|
|
981
|
+
// i contesti che non ricordano il layout restano come prima
|
|
982
|
+
: _jsx(TMPanelManagerWithPersistenceProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'tmSearchResult', isPersistenceEnabled: isLayoutPersisted && hasSearchResultPanelLayout(), persistPanelStates: isLayoutPersisted ? (state) => saveSearchResultPanelLayout(state, disabledLayoutPanelIds) : undefined, persistedPanelStates: isLayoutPersisted ? getSearchResultPanelLayout() : undefined, children: panelManagerContent }) }) }) }), renderDcmtOperations] }));
|
|
859
983
|
};
|
|
860
984
|
export default TMSearchResult;
|
|
861
|
-
|
|
985
|
+
/** Cerca una voce di operazione per id, anche dentro i sottomenu */
|
|
986
|
+
const findOperationItemById = (items, id) => {
|
|
987
|
+
for (const item of items) {
|
|
988
|
+
if (item.id === id)
|
|
989
|
+
return item;
|
|
990
|
+
const found = item.submenu ? findOperationItemById(item.submenu, id) : undefined;
|
|
991
|
+
if (found)
|
|
992
|
+
return found;
|
|
993
|
+
}
|
|
994
|
+
return undefined;
|
|
995
|
+
};
|
|
996
|
+
/**
|
|
997
|
+
* Azioni della colonna di riga. Gli id sono quelli di useDocumentOperations, così il comportamento è
|
|
998
|
+
* lo stesso del menu contestuale; icone e colori sono allineati alle altre griglie mentre il tooltip
|
|
999
|
+
* resta il nome dell'operazione. "Cancella" è la cancellazione logica: ripristino ed eliminazione
|
|
1000
|
+
* fisica restano nel menu contestuale, dove la scelta è esplicita.
|
|
1001
|
+
*/
|
|
1002
|
+
const rowActions = [
|
|
1003
|
+
{ operationId: 'open-form', icon: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) },
|
|
1004
|
+
{ operationId: 'dup', icon: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) },
|
|
1005
|
+
{ operationId: 'del-log', icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }) },
|
|
1006
|
+
];
|
|
1007
|
+
const rowActionsStyles = {
|
|
1008
|
+
cell: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' },
|
|
1009
|
+
icon: { cursor: 'pointer', display: 'flex' },
|
|
1010
|
+
};
|
|
1011
|
+
const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, inputFocusedItem, allowMultipleSelection = true, showExportForm = false, onFocusedItemChanged, onDownloadDcmtsAsync, onVisibleItemChanged, inputSelectedItems = [], lastUpdateSearchTime, searchResult, onSelectionChanged, onDblClick, showSearchTMDatagrid, updateDataColumnsFromDataGrid, updateDataSourceFromDataGrid, updateSelectedRowKeysFromDataGrid, disableAutoFocus = false, dcmtsReturned, dcmtsFound, showCounterConfig = true, counterConfig, showActionsColumn = false, compactPager = false, onRowActionRequest, }) => {
|
|
862
1012
|
const [dataSource, setDataSource] = useState();
|
|
863
1013
|
const [columns, setColumns] = useState([]);
|
|
864
1014
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
@@ -879,8 +1029,8 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
879
1029
|
useEffect(() => {
|
|
880
1030
|
updateDataSourceFromDataGrid(dataSource ?? []);
|
|
881
1031
|
}, [dataSource]);
|
|
882
|
-
const { loadDataListsAsync, renderDataListCell, dataListsCache } = useDataListItem();
|
|
883
|
-
const { loadUsersAsync, renderUserIdViewer, usersCache } = useDataUserIdItem();
|
|
1032
|
+
const { loadDataListsAsync, renderDataListCell, dataListsCache, getDataListItem } = useDataListItem();
|
|
1033
|
+
const { loadUsersAsync, renderUserIdViewer, usersCache, getUserItem, getCompleteUserName } = useDataUserIdItem();
|
|
884
1034
|
useEffect(() => {
|
|
885
1035
|
// Sincronizza focusedItem con inputFocusedItem dal padre
|
|
886
1036
|
if (deepCompare(inputFocusedItem, focusedItem))
|
|
@@ -1112,6 +1262,57 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1112
1262
|
});
|
|
1113
1263
|
return cols;
|
|
1114
1264
|
}, [searchResult, dataType, cellRender, getDisplayFormat, dataListsCache, usersCache]);
|
|
1265
|
+
/**
|
|
1266
|
+
* Colonne in cui il testo mostrato è diverso dal valore archiviato: date e numeri formattati, descrizioni
|
|
1267
|
+
* di liste dati e utenti. Su queste la ricerca della griglia va fatta sul testo mostrato, altrimenti non
|
|
1268
|
+
* trova nulla (es. cercando 29/01/2023 su una data, che nei dati è una stringa ISO).
|
|
1269
|
+
* Le cache sono ref lette al momento della ricerca: a quel punto sono già popolate.
|
|
1270
|
+
*/
|
|
1271
|
+
const searchTextGetters = useMemo(() => {
|
|
1272
|
+
const getters = new Map();
|
|
1273
|
+
const columnsOfResult = searchResult?.dtdResult?.columns;
|
|
1274
|
+
if (!columnsOfResult)
|
|
1275
|
+
return getters;
|
|
1276
|
+
const uniqueKeys = generateUniqueColumnKeys(columnsOfResult, searchResult?.fromTID);
|
|
1277
|
+
columnsOfResult.forEach((col, index) => {
|
|
1278
|
+
const dataField = uniqueKeys[index];
|
|
1279
|
+
if (!dataField)
|
|
1280
|
+
return;
|
|
1281
|
+
const dataDomain = MetadataDataDomains[(col.extendedProperties?.["DataDomain"] ?? "None")];
|
|
1282
|
+
const dataListID = Number(col.extendedProperties?.["DataListID"]);
|
|
1283
|
+
if (dataDomain === MetadataDataDomains.DataList && dataListID) {
|
|
1284
|
+
getters.set(dataField, (rowData) => getDataListItem(dataListID, rowData?.[dataField])?.name ?? String(rowData?.[dataField] ?? ''));
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
if (dataDomain === MetadataDataDomains.UserID) {
|
|
1288
|
+
getters.set(dataField, (rowData) => {
|
|
1289
|
+
const rawValue = rowData?.[dataField];
|
|
1290
|
+
// Un campo utente vuoto non mostra nulla: senza questo controllo Number('') darebbe 0, cioè l'utente di sistema
|
|
1291
|
+
if (rawValue === undefined || rawValue === null || rawValue === '')
|
|
1292
|
+
return '';
|
|
1293
|
+
const userId = Number(rawValue);
|
|
1294
|
+
if (Number.isNaN(userId))
|
|
1295
|
+
return String(rawValue);
|
|
1296
|
+
if (userId === 0)
|
|
1297
|
+
return SDKUI_Localizator.SystemUser ?? '';
|
|
1298
|
+
const ud = getUserItem(userId);
|
|
1299
|
+
return (ud ? getCompleteUserName(ud.domain, ud.name) : undefined) ?? String(rawValue);
|
|
1300
|
+
});
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
// Data e numero vengono mostrati con il formato definito sulle proprietà estese della colonna
|
|
1304
|
+
const { format, formatCulture } = getColumnFormatInfo(col);
|
|
1305
|
+
if (col.dataType === DataColumnTypes.DateTime) {
|
|
1306
|
+
getters.set(dataField, (rowData) => getDateDisplayText(rowData?.[dataField], format, formatCulture));
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
if (col.dataType === DataColumnTypes.Number) {
|
|
1310
|
+
getters.set(dataField, (rowData) => getNumberDisplayText(rowData?.[dataField], format, formatCulture));
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
return getters;
|
|
1314
|
+
}, [searchResult, getDataListItem, getUserItem, getCompleteUserName]);
|
|
1315
|
+
const customizeColumns = useMemo(() => customizeSearchByDisplayText(searchTextGetters), [searchTextGetters]);
|
|
1115
1316
|
/**
|
|
1116
1317
|
* Effect che carica i dati necessari e genera le colonne della griglia.
|
|
1117
1318
|
* Esegue il caricamento delle cache (DataList e UserID) in parallelo prima di generare le colonne,
|
|
@@ -1203,6 +1404,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1203
1404
|
// Era sull'icona, non fare nulla
|
|
1204
1405
|
return;
|
|
1205
1406
|
}
|
|
1407
|
+
// Due click rapidi su un'icona della colonna azioni non devono aprire anche il form
|
|
1408
|
+
if (e.event?.target.closest('.tm-row-actions'))
|
|
1409
|
+
return;
|
|
1206
1410
|
// Verifica che il documento abbia TID e DID validi prima di procedere
|
|
1207
1411
|
if (!e.data.TID || !e.data.DID) {
|
|
1208
1412
|
ShowAlert({
|
|
@@ -1216,8 +1420,41 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1216
1420
|
onFocusedItemChanged?.(e.data);
|
|
1217
1421
|
onDblClick();
|
|
1218
1422
|
}, [onDblClick]);
|
|
1423
|
+
/**
|
|
1424
|
+
* Colonna delle azioni di riga, in testa alla griglia (subito dopo la checkbox di selezione, che
|
|
1425
|
+
* DevExtreme tiene sempre per prima). Vive solo qui e non nello stato `columns`, che è quello
|
|
1426
|
+
* sincronizzato col padre per l'esportazione: una colonna senza dataField finirebbe in Excel come
|
|
1427
|
+
* colonna vuota.
|
|
1428
|
+
*
|
|
1429
|
+
* Le operazioni sono lette da una ref perché `operationItems` è ricostruito a ogni render del
|
|
1430
|
+
* padre: usarlo come dipendenza renderebbe nuova la colonna, e con essa `dataColumns`, ad ogni
|
|
1431
|
+
* giro. La cellRender le rilegge a ogni ridisegno della griglia, quindi resta comunque aggiornata.
|
|
1432
|
+
*/
|
|
1433
|
+
const operationItemsRef = useRef(operationItems);
|
|
1434
|
+
operationItemsRef.current = operationItems;
|
|
1435
|
+
const actionsColumn = useMemo(() => ({
|
|
1436
|
+
caption: '',
|
|
1437
|
+
width: 110,
|
|
1438
|
+
alignment: 'center',
|
|
1439
|
+
allowSorting: false,
|
|
1440
|
+
allowFiltering: false,
|
|
1441
|
+
allowHeaderFiltering: false,
|
|
1442
|
+
allowResizing: false,
|
|
1443
|
+
allowReordering: false,
|
|
1444
|
+
allowHiding: false,
|
|
1445
|
+
allowExporting: false,
|
|
1446
|
+
showInColumnChooser: false,
|
|
1447
|
+
cssClass: 'tm-row-actions',
|
|
1448
|
+
cellRender: (cellData) => (_jsx("div", { style: rowActionsStyles.cell, children: rowActions.map(action => {
|
|
1449
|
+
const operation = findOperationItemById(operationItemsRef.current, action.operationId);
|
|
1450
|
+
if (!operation || operation.visible === false)
|
|
1451
|
+
return null;
|
|
1452
|
+
return (_jsx("span", { style: rowActionsStyles.icon, title: operation.name, onClick: () => onRowActionRequest?.(action.operationId, cellData.data), children: action.icon }, action.operationId));
|
|
1453
|
+
}) })),
|
|
1454
|
+
}), [onRowActionRequest]);
|
|
1219
1455
|
const dataColumns = useMemo(() => {
|
|
1220
1456
|
return [
|
|
1457
|
+
...(showActionsColumn ? [actionsColumn] : []),
|
|
1221
1458
|
{
|
|
1222
1459
|
dataType: "object",
|
|
1223
1460
|
dataField: 'FILEEXT',
|
|
@@ -1231,7 +1468,7 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1231
1468
|
},
|
|
1232
1469
|
...columns.filter(col => col.dataField !== 'FILEEXT')
|
|
1233
1470
|
];
|
|
1234
|
-
}, [columns, onDownloadDcmtsAsync, openInOffice]);
|
|
1471
|
+
}, [columns, onDownloadDcmtsAsync, openInOffice, showActionsColumn, actionsColumn]);
|
|
1235
1472
|
const onContentReady = useCallback((e) => {
|
|
1236
1473
|
if (e === undefined)
|
|
1237
1474
|
return;
|
|
@@ -1242,21 +1479,24 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1242
1479
|
setVisibleItems(visibleData);
|
|
1243
1480
|
}, []);
|
|
1244
1481
|
useEffect(() => { onVisibleItemChanged?.(visibleItems); }, [visibleItems]);
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1482
|
+
const gridCounterConfig = useMemo(() => {
|
|
1483
|
+
if (!showCounterConfig)
|
|
1484
|
+
return { show: false };
|
|
1485
|
+
const defaultItems = dcmtsReturned !== undefined && dcmtsFound !== undefined
|
|
1486
|
+
? new Map([
|
|
1487
|
+
[CounterItemKey.allItems, {
|
|
1488
|
+
key: CounterItemKey.allItems,
|
|
1489
|
+
show: true,
|
|
1490
|
+
caption: `${dcmtsReturned} restituiti su ${dcmtsFound} trovati`,
|
|
1491
|
+
value: `${dcmtsReturned}/${dcmtsFound}`,
|
|
1492
|
+
icon: _jsx(IconAll, {}),
|
|
1493
|
+
order: -1
|
|
1494
|
+
}]
|
|
1495
|
+
])
|
|
1496
|
+
: undefined;
|
|
1497
|
+
return { ...counterConfig, show: true, items: counterConfig?.items ?? defaultItems };
|
|
1498
|
+
}, [showCounterConfig, counterConfig, dcmtsReturned, dcmtsFound]);
|
|
1499
|
+
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 })] });
|
|
1260
1500
|
};
|
|
1261
1501
|
//#region TMSearchResultSelector
|
|
1262
1502
|
const StyledItemTemplate = styled.div `
|
|
@@ -1317,14 +1557,29 @@ const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCa
|
|
|
1317
1557
|
}
|
|
1318
1558
|
}
|
|
1319
1559
|
}, [sortedCategories.length, autoSelectFirst]);
|
|
1560
|
+
// La preselezione esterna va applicata una volta per valore: riapplicarla a ogni cambio di
|
|
1561
|
+
// searchResults faceva sì che un refresh annullasse la navigazione dell'utente.
|
|
1562
|
+
const appliedSelectedTIDRef = useRef(undefined);
|
|
1320
1563
|
useEffect(() => {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1564
|
+
// Richiesta ritirata: dimentichiamo il valore applicato, così se il chiamante richiede di
|
|
1565
|
+
// nuovo lo stesso TID la preselezione riparte
|
|
1566
|
+
if (selectedTID === undefined) {
|
|
1567
|
+
appliedSelectedTIDRef.current = undefined;
|
|
1568
|
+
return;
|
|
1324
1569
|
}
|
|
1570
|
+
if (appliedSelectedTIDRef.current === selectedTID)
|
|
1571
|
+
return;
|
|
1572
|
+
const found = searchResults.find(r => Number(r.fromTID) === selectedTID);
|
|
1573
|
+
if (!found)
|
|
1574
|
+
return; // risultati non ancora arrivati: riproviamo al prossimo giro
|
|
1575
|
+
appliedSelectedTIDRef.current = selectedTID;
|
|
1576
|
+
handleSelect(found);
|
|
1325
1577
|
}, [selectedTID, searchResults]);
|
|
1326
|
-
|
|
1327
|
-
|
|
1578
|
+
// force = true per i click dell'utente: la selezione va notificata anche se il tipo documento è già quello selezionato
|
|
1579
|
+
const handleSelect = useCallback((result, force = false) => {
|
|
1580
|
+
if (result === undefined)
|
|
1581
|
+
return;
|
|
1582
|
+
if (selectedResult !== result || force) {
|
|
1328
1583
|
setSelectedResult(result);
|
|
1329
1584
|
onSelectionChanged?.(result);
|
|
1330
1585
|
}
|
|
@@ -1369,32 +1624,88 @@ const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCa
|
|
|
1369
1624
|
renderItem: (result, isSelected) => (_jsxs("div", { style: { width: '100%', padding: '5px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsx(TMTidViewer, { tid: result.fromTID, did: Number(result.dtdResult?.rows?.[0]?.[1]), showIcon: true }), _jsx("div", { style: { padding: 3, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: 'white', color: 'gray', borderRadius: 3 }, children: result.dcmtsReturned })] })),
|
|
1370
1625
|
itemHeight: 40
|
|
1371
1626
|
})), [sortedCategories, groupedResults, searchResults]);
|
|
1372
|
-
const renderCategoryItems = (category) => (_jsx("div", { style: { padding: '5px', height: '100%', overflow: 'auto' }, children: groupedResults[category].map((result, index) => (_jsx(MemoizedStyledItemTemplate, { "$isSelected": selectedResult === result, onClick: () => handleSelect(result), children: renderItemTemplate(result) }, index))) }));
|
|
1627
|
+
const renderCategoryItems = (category) => (_jsx("div", { style: { padding: '5px', height: '100%', overflow: 'auto' }, children: groupedResults[category].map((result, index) => (_jsx(MemoizedStyledItemTemplate, { "$isSelected": selectedResult === result, onClick: () => handleSelect(result, true), children: renderItemTemplate(result) }, index))) }));
|
|
1373
1628
|
if (disableAccordionIfSingleCategory && sortedCategories.length === 1) {
|
|
1374
1629
|
const category = sortedCategories[0];
|
|
1375
1630
|
return renderCategoryItems(category);
|
|
1376
1631
|
}
|
|
1377
|
-
return (_jsx(TMAccordionNew, { groups: accordionGroups, selectedItem: selectedResult, onSelectedItemChange: (result) => handleSelect(result) }));
|
|
1632
|
+
return (_jsx(TMAccordionNew, { groups: accordionGroups, selectedItem: selectedResult, onSelectedItemChange: (result) => handleSelect(result, true) }));
|
|
1378
1633
|
};
|
|
1379
1634
|
//#endregion TMSearchResultSelector
|
|
1380
|
-
|
|
1381
|
-
|
|
1635
|
+
/** Senza UI: vive dentro il provider e passa a onBack, via ref, l'azione "mostra il pannello lista". */
|
|
1636
|
+
const ShowMainPanelBridge = ({ showMainPanelRef }) => {
|
|
1637
|
+
const { setPanelVisibilityById } = useTMPanelManagerContext();
|
|
1382
1638
|
useEffect(() => {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1639
|
+
// Su mobile mostrare un pannello nasconde gli altri; su desktop la lista è già visibile
|
|
1640
|
+
showMainPanelRef.current = () => setPanelVisibilityById('tmSearchResult', true);
|
|
1641
|
+
return () => { showMainPanelRef.current = null; };
|
|
1642
|
+
}, [setPanelVisibilityById, showMainPanelRef]);
|
|
1643
|
+
return null;
|
|
1644
|
+
};
|
|
1645
|
+
/** Senza UI: allinea i pannelli quando il layout viene salvato da un altro risultato di ricerca già aperto */
|
|
1646
|
+
const SavedLayoutSyncHandler = ({ disabledPanelIds }) => {
|
|
1647
|
+
const { panelVisibility, panelDimensions, maximizedPanels, isResizingActive, setPanelVisibilityById, setPanelDimensionsById } = useTMPanelManagerContext();
|
|
1648
|
+
// Il layout è condiviso fra i risultati aperti: si sottoscrive la firma, che è una stringa, perché
|
|
1649
|
+
// React confronta le letture per riferimento e su un oggetto ri-renderizzerebbe all'infinito
|
|
1650
|
+
const savedLayoutSignature = useSyncExternalStore(subscribeSearchResultPanelLayout, getSearchResultPanelLayoutSignature);
|
|
1651
|
+
useEffect(() => {
|
|
1652
|
+
// Con un pannello massimizzato o un separatore in movimento comanda l'utente di questa istanza
|
|
1653
|
+
if (maximizedPanels.length > 0 || isResizingActive)
|
|
1654
|
+
return;
|
|
1655
|
+
// Al mount panelVisibility è ancora vuota: lo stato iniziale lo applica il provider.
|
|
1656
|
+
// Un pannello disabilitato qui non va aperto, gli altri si allineano comunque
|
|
1657
|
+
const panelsToApply = Object.entries(getSearchResultPanelLayout())
|
|
1658
|
+
.filter(([id, state]) => panelVisibility[id] !== undefined && !(state?.visible && disabledPanelIds.includes(id)));
|
|
1659
|
+
panelsToApply.forEach(([id, state]) => {
|
|
1660
|
+
const isVisible = state?.visible ?? false;
|
|
1661
|
+
if (panelVisibility[id] !== isVisible)
|
|
1662
|
+
setPanelVisibilityById(id, isVisible);
|
|
1663
|
+
});
|
|
1664
|
+
// Le larghezze salvate valgono come pesi, non come percentuali: qui si normalizzano sui soli pannelli
|
|
1665
|
+
// che questo risultato apre davvero, così lo spazio di quelli scartati non resta vuoto
|
|
1666
|
+
const panelsToShow = panelsToApply.filter(([, state]) => state?.visible && parseFloat(state.width) > 0 && parseFloat(state.height) > 0);
|
|
1667
|
+
const weightTotal = panelsToShow.reduce((total, [, state]) => total + parseFloat(state.width), 0);
|
|
1668
|
+
if (weightTotal <= 0)
|
|
1669
|
+
return;
|
|
1670
|
+
// Dopo la visibilità, che accoda un suo ricalcolo delle quote: così restano quelle salvate.
|
|
1671
|
+
// Le quote sono arrotondate, altrimenti due risultati si rincorrono sugli ultimi decimali
|
|
1672
|
+
panelsToShow.forEach(([id, state]) => {
|
|
1673
|
+
const width = `${Math.round(parseFloat(state.width) / weightTotal * 10000) / 100}%`;
|
|
1674
|
+
if (panelDimensions[id]?.width === width && panelDimensions[id]?.height === state.height)
|
|
1675
|
+
return;
|
|
1676
|
+
setPanelDimensionsById(id, width, state.height);
|
|
1677
|
+
});
|
|
1678
|
+
}, [savedLayoutSignature]);
|
|
1679
|
+
return null;
|
|
1680
|
+
};
|
|
1681
|
+
const PanelDisabledStateHandler = ({ isBoardDisabled, savedBlogVisible = false }) => {
|
|
1682
|
+
const { panelVisibility, setPanelVisibilityById, setToolbarButtonDisabled } = useTMPanelManagerContext();
|
|
1683
|
+
const isBlogVisible = panelVisibility['tmBlog'] ?? false;
|
|
1684
|
+
useEffect(() => {
|
|
1685
|
+
setToolbarButtonDisabled('tmBlog', isBoardDisabled);
|
|
1390
1686
|
}, [isBoardDisabled]);
|
|
1687
|
+
/**
|
|
1688
|
+
* Senza bacheca nel tipo documento il pannello va chiuso ogni volta che ricompare, non solo al cambio
|
|
1689
|
+
* di stato: il layout salvato viene applicato dopo questo effetto e può riportarlo a video.
|
|
1690
|
+
* È un effetto di layout per chiuderlo prima che venga disegnato, altrimenti lampeggia.
|
|
1691
|
+
*/
|
|
1692
|
+
useLayoutEffect(() => {
|
|
1693
|
+
if (isBoardDisabled && isBlogVisible)
|
|
1694
|
+
setPanelVisibilityById('tmBlog', false);
|
|
1695
|
+
}, [isBoardDisabled, isBlogVisible]);
|
|
1696
|
+
// Bacheca di nuovo prevista: si riapre come da layout salvato. isBlogVisible non è fra le dipendenze,
|
|
1697
|
+
// altrimenti riaprirebbe il pannello appena chiuso dall'utente
|
|
1698
|
+
useEffect(() => {
|
|
1699
|
+
if (!isBoardDisabled && savedBlogVisible && !isBlogVisible)
|
|
1700
|
+
setPanelVisibilityById('tmBlog', true);
|
|
1701
|
+
}, [isBoardDisabled, savedBlogVisible]);
|
|
1391
1702
|
return null;
|
|
1392
1703
|
};
|
|
1393
|
-
const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, onBack }) => {
|
|
1704
|
+
const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, isPageVisible = true, onBack }) => {
|
|
1394
1705
|
const { setPanelVisibilityById, toggleMaximize, isResizingActive, countVisibleLeafPanels } = useTMPanelManagerContext();
|
|
1395
1706
|
const deviceType = useDeviceType();
|
|
1396
1707
|
const isMobile = deviceType === DeviceType.MOBILE;
|
|
1397
|
-
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 }, refreshPreviewTrigger));
|
|
1708
|
+
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));
|
|
1398
1709
|
};
|
|
1399
1710
|
// Styled Components
|
|
1400
1711
|
const StyledPlaceholder = styled.div `
|