@topconsultnpm/sdkui-react 6.22.0-dev2.4 → 6.22.0-dev2.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/lib/components/NewComponents/ContextMenu/TMContextMenu.js +1 -1
  2. package/lib/components/NewComponents/FloatingMenuBar/TMFloatingMenuBar.js +1 -1
  3. package/lib/components/base/TMButton.d.ts +13 -0
  4. package/lib/components/base/TMButton.js +137 -5
  5. package/lib/components/base/TMDataGrid.d.ts +2 -0
  6. package/lib/components/base/TMDataGrid.js +81 -8
  7. package/lib/components/base/TMEditorBase.d.ts +2 -0
  8. package/lib/components/base/TMFileManager.js +5 -3
  9. package/lib/components/base/TMFileManagerUtils.d.ts +1 -0
  10. package/lib/components/base/TMFileManagerUtils.js +16 -0
  11. package/lib/components/base/TMModal.d.ts +11 -0
  12. package/lib/components/base/TMModal.js +47 -13
  13. package/lib/components/base/TMPanel.d.ts +1 -0
  14. package/lib/components/base/TMPanel.js +79 -20
  15. package/lib/components/base/TMPopUp.js +3 -0
  16. package/lib/components/base/TMTooltip.d.ts +2 -1
  17. package/lib/components/base/TMTooltip.js +23 -6
  18. package/lib/components/base/TMTreeView.js +108 -67
  19. package/lib/components/choosers/TMDcmtTypeChooser.js +4 -3
  20. package/lib/components/choosers/TMDistinctValues.d.ts +9 -7
  21. package/lib/components/choosers/TMDistinctValues.js +149 -142
  22. package/lib/components/choosers/TMMetadataChooser.js +1 -1
  23. package/lib/components/choosers/TMQuickSearchInfo.d.ts +12 -0
  24. package/lib/components/choosers/TMQuickSearchInfo.js +279 -0
  25. package/lib/components/choosers/TMQuickSearchSettingsForm.d.ts +16 -0
  26. package/lib/components/choosers/TMQuickSearchSettingsForm.js +94 -0
  27. package/lib/components/choosers/TMSelectedValuesSummary.d.ts +13 -0
  28. package/lib/components/choosers/TMSelectedValuesSummary.js +91 -0
  29. package/lib/components/editors/TMDropDown.d.ts +3 -0
  30. package/lib/components/editors/TMDropDown.js +197 -5
  31. package/lib/components/editors/TMMetadataEditor.d.ts +5 -1
  32. package/lib/components/editors/TMMetadataEditor.js +8 -3
  33. package/lib/components/editors/TMTextBox.js +2 -2
  34. package/lib/components/features/documents/TMBatchUpdateForm.js +19 -5
  35. package/lib/components/features/documents/TMDcmtBlog.d.ts +3 -0
  36. package/lib/components/features/documents/TMDcmtBlog.js +4 -4
  37. package/lib/components/features/documents/TMDcmtForm.d.ts +7 -0
  38. package/lib/components/features/documents/TMDcmtForm.js +112 -45
  39. package/lib/components/features/documents/TMDcmtFormActionButtons.d.ts +11 -0
  40. package/lib/components/features/documents/TMDcmtFormActionButtons.js +74 -7
  41. package/lib/components/features/documents/TMDcmtPreview.js +10 -6
  42. package/lib/components/features/documents/TMMasterDetailDcmts.js +95 -33
  43. package/lib/components/features/documents/TMMasterInfoFields.d.ts +19 -0
  44. package/lib/components/features/documents/TMMasterInfoFields.js +186 -0
  45. package/lib/components/features/documents/TMMergeToPdfForm.d.ts +1 -2
  46. package/lib/components/features/documents/TMMergeToPdfForm.js +30 -5
  47. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.d.ts +45 -0
  48. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +323 -0
  49. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +370 -0
  50. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +283 -0
  51. package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
  52. package/lib/components/features/documents/TMRelationViewer.js +69 -20
  53. package/lib/components/features/search/TMSearch.js +31 -18
  54. package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
  55. package/lib/components/features/search/TMSearchQueryPanel.d.ts +2 -1
  56. package/lib/components/features/search/TMSearchQueryPanel.js +45 -9
  57. package/lib/components/features/search/TMSearchResult.d.ts +15 -0
  58. package/lib/components/features/search/TMSearchResult.js +509 -187
  59. package/lib/components/features/search/TMTreeSelector.js +18 -5
  60. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
  61. package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
  62. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  63. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
  64. package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
  65. package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
  66. package/lib/components/layout/panelManager/types.d.ts +3 -0
  67. package/lib/components/layout/panelManager/utils.js +13 -11
  68. package/lib/components/viewers/TMMidViewer.js +4 -1
  69. package/lib/components/viewers/TMTidViewer.js +25 -11
  70. package/lib/devextreme-license.d.ts +1 -0
  71. package/lib/devextreme-license.js +3 -0
  72. package/lib/helper/SDKUI_Globals.d.ts +109 -0
  73. package/lib/helper/SDKUI_Globals.js +150 -0
  74. package/lib/helper/SDKUI_Localizator.d.ts +34 -0
  75. package/lib/helper/SDKUI_Localizator.js +340 -0
  76. package/lib/helper/TMIcons.d.ts +1 -0
  77. package/lib/helper/TMIcons.js +3 -0
  78. package/lib/helper/dataGridSearchHelper.d.ts +28 -0
  79. package/lib/helper/dataGridSearchHelper.js +51 -0
  80. package/lib/helper/dcmtsHelper.d.ts +20 -0
  81. package/lib/helper/dcmtsHelper.js +58 -1
  82. package/lib/helper/helpers.d.ts +20 -0
  83. package/lib/helper/helpers.js +55 -1
  84. package/lib/helper/index.d.ts +1 -0
  85. package/lib/helper/index.js +1 -0
  86. package/lib/helper/queryHelper.d.ts +2 -0
  87. package/lib/helper/queryHelper.js +3 -1
  88. package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
  89. package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
  90. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  91. package/lib/hooks/useArchiveListForm.js +46 -0
  92. package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
  93. package/lib/hooks/useCaseFlowApprove.js +157 -0
  94. package/lib/hooks/useDataUserIdItem.d.ts +1 -0
  95. package/lib/hooks/useDataUserIdItem.js +1 -0
  96. package/lib/hooks/useDocumentOperations.d.ts +4 -0
  97. package/lib/hooks/useDocumentOperations.js +41 -7
  98. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +113 -0
  99. package/lib/hooks/useMultiMasterDetailDcmts.js +760 -0
  100. package/lib/hooks/useRelatedDocuments.js +2 -26
  101. package/lib/hooks/useShowDossiersV5.d.ts +12 -0
  102. package/lib/hooks/useShowDossiersV5.js +37 -0
  103. package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
  104. package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
  105. package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
  106. package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
  107. package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
  108. package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
  109. package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
  110. package/lib/hooks/useTMDistinctValuesSource.js +105 -0
  111. package/lib/index.d.ts +2 -0
  112. package/lib/index.js +5 -2
  113. package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
  114. package/lib/services/caseflow/CaseFlowService.js +693 -0
  115. package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
  116. package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
  117. package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
  118. package/lib/services/caseflow/caseFlowNameCache.js +116 -0
  119. package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
  120. package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
  121. package/lib/services/index.d.ts +4 -0
  122. package/lib/services/index.js +4 -0
  123. package/lib/services/platform_services.d.ts +1 -1
  124. package/package.json +9 -5
@@ -0,0 +1,323 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
+ import { LayoutModes } from "@topconsultnpm/sdk-ts";
4
+ import { LoadIndicator } from "devextreme-react/load-indicator";
5
+ import { SearchResultContext } from "../../../ts";
6
+ import { getFullFileExtension, getMultiDetailModalSize, getMultiDetailPanelStates, hasMultiDetailPanelStates, hoverStyle, IconArchive, IconArrowDown, IconArrowUp, IconDelete, IconDuplicate, IconEdit, IconEraser, IconInfo, IconMenuVertical, IconRefresh, IconSearch, IconSearchCheck, saveMultiDetailModalSize, saveMultiDetailPanelStates, SDKUI_Localizator } from "../../../helper";
7
+ import { TMColors } from "../../../utils/theme";
8
+ import { DeviceType, useDeviceType } from "../../base/TMDeviceProvider";
9
+ import { TMLayoutWaitingContainer } from "../../base/TMWaitPanel";
10
+ import TMModal from "../../base/TMModal";
11
+ import TMDataGrid from "../../base/TMDataGrid";
12
+ import TMTooltip from "../../base/TMTooltip";
13
+ import { TMDcmtTypeIcon } from "../../viewers/TMTidViewer";
14
+ import { ContextMenu } from "../../NewComponents/ContextMenu";
15
+ import { TMPanelManagerWithPersistenceProvider } from "../../layout/panelManager/TMPanelManagerWithPersistenceProvider";
16
+ import TMPanelManagerContainer from "../../layout/panelManager/TMPanelManagerContainer";
17
+ import TMSearchResult from "../search/TMSearchResult";
18
+ import TMDcmtForm from "./TMDcmtForm";
19
+ import TMDcmtIcon from "./TMDcmtIcon";
20
+ import TMMasterInfoFields from "./TMMasterInfoFields";
21
+ import { MultiDetailArchivePhase, useMultiMasterDetailDcmts } from "../../../hooks/useMultiMasterDetailDcmts";
22
+ import { accordionStyles, getDtdDisplayName, INLINE_EDITABLE_CELL_CLASS, multiDetailStyles, PANEL_GUTTER_PX, renderAccordionHeader, renderSelectDcmtType, renderStepper, SECTION_HEADER_HEIGHT_PX, sectionPanelHeights, TMSectionPanelsHeights, TMSummaryResizeTracker, TMSummarySectionSlot } from "./TMMultiMasterDetailDcmtsUtils";
23
+ // Stili delle sezioni in TMMultiMasterDetailDcmtsUtils (vedi accordionStyles e multiDetailStyles)
24
+ const styles = {
25
+ content: { display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 },
26
+ header: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '6px 12px', borderBottom: `1px solid ${TMColors.card_background}`, flexShrink: 0 },
27
+ // Opzioni nella barra del titolo: stesse misure e colore della chiusura DevExtreme (.dx-popup-title .dx-closebutton)
28
+ titleMenuIcon: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: '18px', height: '18px', margin: '1px 0 0', color: `var(--dx-color-text, ${TMColors.text_normal})`, cursor: 'pointer' },
29
+ titleMenuIconDisabled: { cursor: 'default', opacity: 0.4 },
30
+ titleActions: { display: 'inline-flex', alignItems: 'center', gap: '10px' },
31
+ loader: { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center' },
32
+ summaryContainer: { display: 'flex', flexDirection: 'column', flex: 1, padding: '8px 12px 10px', minHeight: 0, gap: '8px' },
33
+ // I pannelli ripartiscono l'altezza in percentuale: serve un'altezza definita che non dipenda dal
34
+ // contenuto, da qui il riempimento in posizione assoluta dello spazio residuo
35
+ panelsHost: { position: 'relative', flex: 1, minHeight: 0, minWidth: 0 },
36
+ panelsHostFill: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0, display: 'flex' },
37
+ gridWrapper: { position: 'relative', flex: 1, minHeight: 0 },
38
+ actionsCell: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' },
39
+ actionIcon: { cursor: 'pointer', display: 'flex' },
40
+ fileCell: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
41
+ fab: { position: 'absolute', bottom: '18px', right: '20px', width: '42px', height: '42px', borderRadius: '50%', backgroundColor: TMColors.button_floating_background, color: '#fff', border: 'none', cursor: 'pointer', boxShadow: '0 2px 6px rgba(0,0,0,0.2)', zIndex: 1000, transition: 'background-color 0.3s ease, transform 0.2s ease', display: 'flex', justifyContent: 'center', alignItems: 'center' },
42
+ footer: { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center', flexWrap: 'wrap', gap: '14px', paddingTop: '8px', borderTop: `1px solid ${TMColors.card_background}`, flexShrink: 0 },
43
+ footerLabel: { display: 'inline-flex', alignItems: 'center', gap: '8px', cursor: 'pointer', color: TMColors.label_normal, fontSize: '12px', userSelect: 'none', padding: '5px 10px', borderRadius: '16px', background: 'transparent', border: `1px solid ${TMColors.card_background}`, transition: 'all 0.15s ease' },
44
+ archiveButton: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '8px', height: '38px', padding: '0 22px', border: 'none', borderRadius: '10px', fontSize: '14px', fontWeight: 600, letterSpacing: '0.2px', color: '#ffffff', cursor: 'pointer', transition: 'transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease' },
45
+ checkbox: { width: '16px', height: '16px', accentColor: TMColors.primary, cursor: 'pointer', margin: 0 },
46
+ };
47
+ /** Sezione mostrata per prima su mobile, dove i pannelli si vedono uno alla volta */
48
+ const MOBILE_SECTION = 'archiveList';
49
+ const TMMultiMasterDetailDcmtsForm = (props) => {
50
+ const { dtd, masterDcmt, onClose, canNavigateMaster, onNavigateMaster, 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;
51
+ const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, isMasterNavigationEnabled, canNavigateMasterPrev, canNavigateMasterNext, navigateMaster, 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, canNavigateMaster, onNavigateMaster });
52
+ const deviceType = useDeviceType();
53
+ // Su mobile il panel manager mostra un pannello alla volta: le sezioni si raggiungono dalla sua barra
54
+ const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
55
+ // Un pannello alla volta non lascia spazio da cedere: le sezioni chiuse nella vista impilata si
56
+ // aprono qui, altrimenti il pannello a schermo mostrerebbe la sola testata
57
+ useEffect(() => {
58
+ if (isMobile && isSummaryReady)
59
+ openAllSections();
60
+ }, [isMobile, isSummaryReady]);
61
+ // Le altezze sono una scelta dell'utente solo da quando sposta un separatore: quelle calcolate all'apertura
62
+ // o al collasso di una sezione non vanno salvate. Con un layout già salvato lo sono comunque
63
+ const hasUserResizedRef = useRef(false);
64
+ useEffect(() => { hasUserResizedRef.current = hasMultiDetailPanelStates(); }, [layoutResetToken]);
65
+ const persistPanelStatesOnUserResize = useCallback((panelStates) => {
66
+ if (!hasUserResizedRef.current)
67
+ return;
68
+ saveMultiDetailPanelStates(panelStates);
69
+ setHasSavedLayout(true);
70
+ }, []);
71
+ // Ricerca nella griglia dei dettagli da archiviare, come in TMSearch (vedi useDocumentOperations)
72
+ const [showGridSearch, setShowGridSearch] = useState(false);
73
+ const toggleGridSearchItem = {
74
+ name: showGridSearch ? SDKUI_Localizator.HideSearch : SDKUI_Localizator.ShowSearch,
75
+ icon: _jsx(IconSearch, { fontSize: 18 }),
76
+ beginGroup: true,
77
+ onClick: () => setShowGridSearch(prev => !prev),
78
+ };
79
+ const isSummaryPhase = phase === MultiDetailArchivePhase.ArchiveSummary;
80
+ // Dimensioni del modale, ricordate per passo. Si salvano senza aggiornare lo stato: il cambio di
81
+ // dimensioni ricentrerebbe il popup appena ridimensionato dall'utente
82
+ const modalStep = isSummaryPhase ? 'archiveSummary' : 'selectDcmtType';
83
+ const defaultModalSize = isSummaryPhase ? { width: '95%', height: '95%' } : { width: '720px', height: 'auto' };
84
+ const modalSize = useMemo(() => getMultiDetailModalSize(modalStep) ?? defaultModalSize, [modalStep, layoutResetToken]);
85
+ const persistModalSize = useCallback((size) => {
86
+ saveMultiDetailModalSize(modalStep, { width: `${size.width}px`, height: `${size.height}px` });
87
+ setHasSavedLayout(true);
88
+ }, [modalStep, setHasSavedLayout]);
89
+ const baseTitle = SDKUI_Localizator.ArchiveMultipleDetailDocuments;
90
+ // Con un solo tipo il nome è mostrato direttamente nel titolo del modale
91
+ const modalTitle = !hasMultipleTypes && selectedItem ? `${baseTitle}: ${getDtdDisplayName(selectedItem.detailDtd)}` : baseTitle;
92
+ const isArchiveDisabled = isArchiving || currentRows.length === 0;
93
+ const archiveButtonCaption = currentRows.length > 0
94
+ ? `${SDKUI_Localizator.Archive} ${currentRows.length} ${currentRows.length === 1 ? 'dettaglio' : 'dettagli'}`
95
+ : SDKUI_Localizator.Archive;
96
+ const steps = [
97
+ ...(hasMultipleTypes ? [{ phase: MultiDetailArchivePhase.SelectDcmtType, label: SDKUI_Localizator.DcmtType }] : []),
98
+ {
99
+ phase: MultiDetailArchivePhase.ArchiveSummary,
100
+ // Nel riepilogo lo step mostra il tipo selezionato (icona + nome)
101
+ label: selectedItem
102
+ ? (_jsxs("span", { style: multiDetailStyles.stepTypeLabel, children: [_jsx(TMDcmtTypeIcon, { dtd: selectedItem.detailDtd }), _jsx("span", { style: multiDetailStyles.stepTypeLabelText, children: getDtdDisplayName(selectedItem.detailDtd) })] }))
103
+ : SDKUI_Localizator.Summary,
104
+ },
105
+ ];
106
+ // Colonne della griglia: icona del documento, metadati del tipo, file e azioni
107
+ const gridColumns = useMemo(() => {
108
+ const iconColumn = {
109
+ caption: '', width: 50, alignment: 'center',
110
+ allowSorting: false, allowFiltering: false, allowResizing: false, allowReordering: false, allowHiding: false, allowEditing: false,
111
+ cellRender: (cell) => {
112
+ const file = cell.data?.__file;
113
+ return _jsx(TMDcmtIcon, { fileExtension: file ? getFullFileExtension(file.name) : undefined, fileCount: file ? 1 : 0, tooltipContent: file?.name });
114
+ },
115
+ };
116
+ const fileColumn = {
117
+ caption: 'File', width: 220, allowSorting: false, allowEditing: false,
118
+ cellRender: (cell) => {
119
+ const file = cell.data?.__file;
120
+ return _jsx("span", { style: { ...styles.fileCell, color: file ? TMColors.text_normal : TMColors.label_normal }, children: file ? file.name : '' });
121
+ },
122
+ };
123
+ const actionsColumn = {
124
+ caption: '', width: 110, alignment: 'center',
125
+ allowSorting: false, allowFiltering: false, allowHeaderFiltering: false, allowResizing: false,
126
+ allowReordering: false, allowHiding: false, showInColumnChooser: false, allowEditing: false,
127
+ cssClass: 'tm-row-actions',
128
+ 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 }) }) })] })),
129
+ };
130
+ return [actionsColumn, iconColumn, ...detailColumns, ...(detailSupportsFile ? [fileColumn] : [])];
131
+ }, [detailColumns, detailSupportsFile]);
132
+ // ----- Griglia dei dettagli: opzioni e callback con identità stabile -------------------------
133
+ // Un nuovo riferimento a ogni render farebbe rifare a DevExtreme il setup di selezione/editing
134
+ const gridSelection = useMemo(() => ({ mode: 'single', showCheckBoxesMode: 'none' }), []);
135
+ const gridEditing = useMemo(() => ({
136
+ mode: 'cell',
137
+ allowUpdating: true,
138
+ allowAdding: false,
139
+ allowDeleting: false,
140
+ startEditAction: 'click',
141
+ selectTextOnEditStart: false,
142
+ }), []);
143
+ const handleSelectionChanged = useCallback((e) => {
144
+ setSelectedRowKeys(e.selectedRowKeys ?? []);
145
+ }, [setSelectedRowKeys]);
146
+ const handleFocusedRowChanged = useCallback((e) => {
147
+ setFocusedRowKey(e.row?.key);
148
+ }, [setFocusedRowKey]);
149
+ const handleRowUpdating = useCallback((e) => {
150
+ updateRowValues(e.key, e.newData);
151
+ }, [updateRowValues]);
152
+ // Il doppio clic apre il form della riga, tranne su azioni e celle modificabili in linea
153
+ const handleCellDblClick = useCallback((e) => {
154
+ if (e.rowType !== 'data')
155
+ return;
156
+ const cssClass = e.column?.cssClass ?? '';
157
+ if (cssClass === 'tm-row-actions' || cssClass === INLINE_EDITABLE_CELL_CLASS)
158
+ return;
159
+ openEditForm(e.data);
160
+ }, [openEditForm]);
161
+ // Menu contestuale con le stesse azioni della colonna
162
+ const contextMenuItems = useMemo(() => [
163
+ { name: SDKUI_Localizator.Update, icon: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }), operationType: 'singleRow', onClick: (id) => openEditForm(archiveRows.find(r => r.id === id)) },
164
+ { name: SDKUI_Localizator.Duplicate, icon: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }), operationType: 'singleRow', onClick: (id) => duplicateRow(archiveRows.find(r => r.id === id)) },
165
+ { 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)) },
166
+ toggleGridSearchItem,
167
+ ], [archiveRows, showGridSearch]);
168
+ // Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo.
169
+ // Con il layout di default non c'è niente da cancellare, quindi la voce non compare
170
+ const optionsMenuItems = useMemo(() => hasSavedLayout
171
+ ? [{ name: SDKUI_Localizator.RemoveLayout, icon: _jsx(IconEraser, { fontSize: 18, color: TMColors.error }), onClick: clearLayout }]
172
+ : [], [clearLayout, hasSavedLayout]);
173
+ // Le opzioni riguardano i pannelli del riepilogo: fuori da quella fase non hanno effetto, e su mobile
174
+ // non c'è un layout da cancellare (i pannelli si vedono uno alla volta, a tutta altezza)
175
+ const renderOptionsMenu = () => {
176
+ // Senza voci non si mostra il pulsante, per non aprire un menu vuoto
177
+ if (!isSummaryPhase || isMobile || optionsMenuItems.length === 0)
178
+ return undefined;
179
+ return (_jsx(ContextMenu, { trigger: 'left', items: optionsMenuItems, children: _jsx(TMTooltip, { content: SDKUI_Localizator.Options, children: _jsx("span", { style: styles.titleMenuIcon, "aria-label": SDKUI_Localizator.Options, children: _jsx(IconMenuVertical, { fontSize: 16, color: "white" }) }) }) }));
180
+ };
181
+ const renderMasterNavigation = () => {
182
+ if (!isMasterNavigationEnabled)
183
+ return undefined;
184
+ const renderArrow = (dir, isEnabled, icon, tooltip) => (_jsx(TMTooltip, { content: tooltip, children: _jsx("span", { role: 'button', "aria-label": tooltip, "aria-disabled": !isEnabled, onClick: isEnabled ? () => navigateMaster(dir) : undefined, style: { ...styles.titleMenuIcon, ...(isEnabled ? {} : styles.titleMenuIconDisabled) }, children: icon }) }));
185
+ return (_jsxs(_Fragment, { children: [renderArrow('prev', canNavigateMasterPrev, _jsx(IconArrowUp, { fontSize: 16, color: "white" }), SDKUI_Localizator.Previous), renderArrow('next', canNavigateMasterNext, _jsx(IconArrowDown, { fontSize: 16, color: "white" }), SDKUI_Localizator.Next)] }));
186
+ };
187
+ // Azioni nella barra del titolo: navigazione del master e opzioni del layout
188
+ const renderTitleActions = () => {
189
+ const navigation = renderMasterNavigation();
190
+ const optionsMenu = renderOptionsMenu();
191
+ if (!navigation && !optionsMenu)
192
+ return undefined;
193
+ return _jsxs("div", { style: styles.titleActions, children: [navigation, optionsMenu] });
194
+ };
195
+ // ----- Sezione: informazioni del master -----------------------------------------------------
196
+ const renderMasterInfo = (panel) => {
197
+ const isOpen = sectionsOpen.masterInfo;
198
+ const editMasterButton = canUpdateMaster
199
+ ? (_jsx(TMTooltip, { content: `${SDKUI_Localizator.Update}: ${getDtdDisplayName(dtd)}`, children: _jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); setIsOpenMasterForm(true); }, style: accordionStyles.accordionExpandBtn, ...hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal }), children: _jsx(IconEdit, { fontSize: 16 }) }) }))
200
+ : undefined;
201
+ return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
202
+ section: 'masterInfo',
203
+ isOpen,
204
+ tooltip: isOpen ? SDKUI_Localizator.HideMasterInfo : SDKUI_Localizator.ShowMasterInfo,
205
+ count: masterFieldsCount,
206
+ title: (_jsxs(_Fragment, { children: [_jsx(TMDcmtTypeIcon, { dtd: dtd }), _jsxs("span", { style: multiDetailStyles.stepTypeLabelText, children: ["Master: ", getDtdDisplayName(dtd)] })] })),
207
+ actions: editMasterButton,
208
+ panel,
209
+ onToggleOpen: setSectionOpen,
210
+ }), isOpen && (_jsx(TMMasterInfoFields, { dtd: dtd, dcmt: masterDcmt, getValuesByMidAsync: getMasterMetadataAsync, reloadToken: masterInfoReloadToken, isExpandedView: panel.isMaximized }))] }));
211
+ };
212
+ // ----- Sezione: dettagli già archiviati -----------------------------------------------------
213
+ const renderArchivedDetails = (panel) => {
214
+ const isOpen = sectionsOpen.archivedDetails;
215
+ const hasResults = archivedDetailResults.length > 0 && archivedDetailsCount > 0;
216
+ // Ricarica i dettagli già archiviati: la sezione va aperta, altrimenti l'esito non sarebbe visibile
217
+ const refreshArchivedButton = (_jsx(TMTooltip, { content: SDKUI_Localizator.Refresh, children: _jsx("button", { type: 'button', onClick: (e) => { e.stopPropagation(); setSectionOpen('archivedDetails', true); refreshArchivedDetailsAsync(); }, disabled: isLoadingArchivedDetails, style: { ...accordionStyles.accordionExpandBtn, ...(isLoadingArchivedDetails ? { cursor: 'default', opacity: 0.5 } : {}) }, "aria-label": SDKUI_Localizator.Refresh, ...(isLoadingArchivedDetails ? {} : hoverStyle({ background: TMColors.primary_container, color: TMColors.primary }, { background: 'transparent', color: TMColors.label_normal })), children: _jsx(IconRefresh, { fontSize: 16 }) }) }));
218
+ return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
219
+ section: 'archivedDetails',
220
+ isOpen,
221
+ tooltip: isOpen ? SDKUI_Localizator.HideArchivedDetails : SDKUI_Localizator.ShowArchivedDetails,
222
+ count: archivedDetailsCount,
223
+ title: (_jsxs(_Fragment, { children: [_jsx(IconSearchCheck, { fontSize: 15 }), _jsx("span", { style: multiDetailStyles.stepTypeLabelText, children: SDKUI_Localizator.ArchivedDetails })] })),
224
+ actions: refreshArchivedButton,
225
+ panel,
226
+ onToggleOpen: setSectionOpen,
227
+ }), isOpen && (_jsx("div", { style: { ...accordionStyles.accordionListBody, ...(panel.isMaximized ? { borderTop: 'none' } : {}) }, children: isLoadingArchivedDetails
228
+ ? _jsxs("div", { style: accordionStyles.accordionListPlaceholder, children: [_jsx(LoadIndicator, { height: 30, width: 30 }), SDKUI_Localizator.Loading] })
229
+ : (hasResults
230
+ ? _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 })
231
+ : _jsx("div", { style: accordionStyles.accordionListPlaceholder, children: SDKUI_Localizator.NoDataToDisplay })) }))] }));
232
+ };
233
+ // ----- Sezione: dettagli da archiviare ------------------------------------------------------
234
+ const renderArchiveList = (panel) => {
235
+ const isOpen = sectionsOpen.archiveList;
236
+ return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
237
+ section: 'archiveList',
238
+ isOpen,
239
+ tooltip: isOpen ? SDKUI_Localizator.HideDetailsToArchive : SDKUI_Localizator.ShowDetailsToArchive,
240
+ count: currentRows.length,
241
+ title: (_jsxs(_Fragment, { children: [_jsx(IconArchive, { fontSize: 15 }), _jsx("span", { style: multiDetailStyles.stepTypeLabelText, children: SDKUI_Localizator.DetailsToArchive })] })),
242
+ panel,
243
+ onToggleOpen: setSectionOpen,
244
+ }), isOpen && (_jsxs("div", { style: { ...styles.gridWrapper, ...(panel.isMaximized ? {} : { borderTop: `1px solid ${TMColors.card_background}` }) }, children: [_jsx(TMDataGrid, { dataColumns: gridColumns, dataSource: currentRows, keyExpr: "id", height: "100%", showSearchPanel: showGridSearch, showGroupPanel: false, showFilterPanel: false, showHeaderFilter: true, selection: gridSelection, selectedRowKeys: selectedRowKeys, onSelectionChanged: handleSelectionChanged, focusedRowKey: focusedRowKey, onFocusedRowChanged: handleFocusedRowChanged, onCellDblClick: handleCellDblClick, editing: gridEditing, onRowUpdating: handleRowUpdating, customContextMenuItems: contextMenuItems }), _jsx("button", { type: "button", onClick: openAddForm, style: styles.fab, ...hoverStyle({ backgroundColor: '#D94A9F', transform: 'scale(1.1)', boxShadow: '0 4px 12px rgba(37, 89, 165, 0.6)' }, { backgroundColor: TMColors.button_floating_background, transform: 'scale(1)', boxShadow: '0 2px 6px rgba(0,0,0,0.2)' }), children: _jsx(TMTooltip, { content: SDKUI_Localizator.Add, children: _jsx("i", { className: "dx-icon-add", style: { fontSize: '22px' } }) }) })] }))] }));
245
+ };
246
+ // ----- Pannelli del riepilogo, uno per sezione ----------------------------------------------
247
+ const renderSectionsInPanels = () => {
248
+ const definitions = [
249
+ { section: 'masterInfo', name: `Master: ${getDtdDisplayName(dtd)}`, icon: _jsx(IconInfo, { fontSize: 22 }), count: masterFieldsCount, render: renderMasterInfo },
250
+ ...(selectedRelation?.detailTID !== undefined
251
+ ? [{ section: 'archivedDetails', name: SDKUI_Localizator.ArchivedDetails, icon: _jsx(IconSearchCheck, { fontSize: 22 }), count: archivedDetailsCount, render: renderArchivedDetails }]
252
+ : []),
253
+ { section: 'archiveList', name: SDKUI_Localizator.DetailsToArchive, icon: _jsx(IconArchive, { fontSize: 22 }), count: currentRows.length, render: renderArchiveList },
254
+ ];
255
+ const panels = definitions.map((definition, index) => ({
256
+ id: definition.section,
257
+ name: definition.name,
258
+ currentGroupDirection: 'vertical',
259
+ contentOptions: {
260
+ // Ingrandire una sezione collassata la apre: altrimenti il pannello a tutto schermo sarebbe vuoto
261
+ component: _jsx(TMSummarySectionSlot, { section: definition.section, render: definition.render, onBeforeMaximize: (section) => setSectionOpen(section, true) }),
262
+ // Una sezione chiusa vale 0% di altezza: è questo minimo a tenerne a schermo la testata
263
+ minHeight: `${SECTION_HEADER_HEIGHT_PX}px`,
264
+ // La sezione collassata è alta quanto la sua testata: i separatori non la ingrandiscono
265
+ isCollapsed: !sectionsOpen[definition.section],
266
+ },
267
+ // Barra dei pannelli: su mobile è l'unico modo per passare da una sezione all'altra
268
+ toolbarOptions: { icon: definition.icon, visible: true, isActive: true, orderNumber: index + 1, count: definition.count, tooltip: definition.name },
269
+ }));
270
+ const sections = panels.map(panel => panel.id);
271
+ // Le altezze salvate sono il punto di partenza, ma è il collasso corrente a decidere le quote
272
+ const savedStates = getMultiDetailPanelStates();
273
+ const savedDimensions = hasMultiDetailPanelStates()
274
+ ? sections.reduce((acc, section) => {
275
+ if (savedStates[section])
276
+ acc[section] = { width: savedStates[section].width, height: savedStates[section].height };
277
+ return acc;
278
+ }, {})
279
+ : undefined;
280
+ // Con un pannello alla volta non ci sono quote da spartire: quello a schermo prende tutta l'altezza.
281
+ // Le quote proporzionali (e il layout salvato) valgono solo per i pannelli impilati
282
+ const fullHeights = sections.reduce((acc, section) => { acc[section] = { width: '100%', height: '100%' }; return acc; }, {});
283
+ const initialDimensions = isMobile ? fullHeights : sectionPanelHeights(sections, sectionsOpen, savedDimensions);
284
+ const initialVisibility = sections.reduce((acc, section) => { acc[section] = isMobile ? section === MOBILE_SECTION : true; return acc; }, {});
285
+ return (_jsx("div", { style: styles.panelsHost, children: _jsx("div", { style: styles.panelsHostFill, children: _jsxs(TMPanelManagerWithPersistenceProvider
286
+ // Le dimensioni sono lette solo al montaggio: dopo la cancellazione del layout, e al
287
+ // passaggio fra impilato e un pannello alla volta, i pannelli vanno rimontati
288
+ , { panels: panels, initialVisibility: initialVisibility, defaultDimensions: initialDimensions, initialDimensions: initialDimensions, initialMobilePanelId: MOBILE_SECTION,
289
+ // Su mobile il layout è imposto dal dispositivo: salvarlo sovrascriverebbe quello dell'utente
290
+ isPersistenceEnabled: !isMobile,
291
+ // Le altezze si salvano solo dopo che l'utente ha spostato un separatore (vedi TMSummaryResizeTracker)
292
+ persistPanelStates: !isMobile ? persistPanelStatesOnUserResize : undefined, children: [_jsx(TMSummaryResizeTracker, { onUserResize: () => { hasUserResizedRef.current = true; } }), _jsx(TMSectionPanelsHeights, { sections: sections, sectionsOpen: sectionsOpen }), _jsx(TMPanelManagerContainer, { panels: panels, direction: 'vertical', showToolbar: isMobile, minPanelSizePx: SECTION_HEADER_HEIGHT_PX, gutterSizePx: isMobile ? undefined : PANEL_GUTTER_PX })] }, `${layoutResetToken}-${isMobile}`) }) }));
293
+ };
294
+ const renderArchiveSummary = () => (_jsxs("div", { style: styles.summaryContainer, children: [renderSectionsInPanels(), _jsxs("div", { style: styles.footer, children: [_jsxs("label", { style: styles.footerLabel, ...hoverStyle({ borderColor: TMColors.primary, color: TMColors.text_normal }, { borderColor: TMColors.card_background, color: TMColors.label_normal }), children: [_jsx("input", { type: "checkbox", checked: stopOnFirstError, onChange: (e) => setStopOnFirstError(e.target.checked), style: styles.checkbox }), SDKUI_Localizator.StopOnFirstError] }), _jsxs("button", { type: "button", onClick: archiveAllAsync, disabled: isArchiveDisabled, style: {
295
+ ...styles.archiveButton,
296
+ background: isArchiveDisabled ? TMColors.disabled : TMColors.success,
297
+ boxShadow: isArchiveDisabled ? 'none' : `0 4px 14px ${TMColors.success}55`,
298
+ cursor: isArchiveDisabled ? 'default' : 'pointer',
299
+ opacity: isArchiveDisabled ? 0.6 : 1,
300
+ }, ...(isArchiveDisabled ? {} : hoverStyle({ transform: 'translateY(-2px)', boxShadow: `0 6px 20px ${TMColors.success}77`, filter: 'brightness(1.06)' }, { transform: 'translateY(0)', boxShadow: `0 4px 14px ${TMColors.success}55`, filter: 'brightness(1)' })), children: [_jsx(IconArchive, {}), archiveButtonCaption] })] })] }));
301
+ // ----- Render principale --------------------------------------------------------------------
302
+ if (isLoading)
303
+ return null;
304
+ 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,
305
+ // Cancellato il layout il popup torna alle dimensioni di default
306
+ sizeResetToken: layoutResetToken, title: modalTitle, hidePopup: false, askClosingConfirm: archiveRows.length > 0, recenterOnResize: true, titleActions: renderTitleActions(), children: _jsxs("div", { style: styles.content, children: [hasMultipleTypes && _jsx("div", { style: styles.header, children: renderStepper({
307
+ steps,
308
+ phase,
309
+ hasMultipleTypes,
310
+ isBackLocked,
311
+ showBackArrow: isSummaryPhase,
312
+ onBack: backToTypeSelection,
313
+ onStepClick: (stepPhase) => (stepPhase === MultiDetailArchivePhase.SelectDcmtType ? backToTypeSelection() : setPhase(stepPhase)),
314
+ }) }), !isSummaryPhase
315
+ ? renderSelectDcmtType({ detailRelationItems, selectedRelation, onSelectRelation: selectRelation })
316
+ : (isSummaryReady
317
+ ? renderArchiveSummary()
318
+ : _jsx("div", { style: styles.loader, children: _jsx(LoadIndicator, { height: 40, width: 40 }) }))] }) }) }), isOpenAddForm && selectedRelation?.detailTID && (_jsx(TMDcmtForm, { isModal: true, titleModal: getDtdDisplayName(selectedItem?.detailDtd), TID: selectedRelation.detailTID, layoutMode: LayoutModes.Ark, showBackButton: false, allowButtonsRefs: false, inputMids: formInputMids, inputFile: editingRow?.__file ?? null, archiveListMode: {
319
+ isEditingRow: editingRowId !== undefined,
320
+ onUpsertRow: (values, file) => upsertRow(values, file),
321
+ }, onClose: closeAddForm, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showDcmtFormSidebar: showDcmtFormSidebar, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager })), isOpenMasterForm && masterDcmt?.TID !== undefined && masterDcmt?.DID !== undefined && (_jsx(TMDcmtForm, { isModal: true, titleModal: `${SDKUI_Localizator.Update}: ${getDtdDisplayName(dtd)}`, TID: masterDcmt.TID, DID: masterDcmt.DID, layoutMode: LayoutModes.Update, showBackButton: false, allowRelations: false, allowButtonsRefs: false, onSavedAsyncCallback: handleMasterSavedAsync, onClose: () => setIsOpenMasterForm(false), allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, onTaskCreateRequest: onTaskCreateRequest, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showDcmtFormSidebar: showDcmtFormSidebar, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick }))] });
322
+ };
323
+ export default TMMultiMasterDetailDcmtsForm;