@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.
Files changed (118) 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 +4 -0
  12. package/lib/components/base/TMModal.js +42 -11
  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/TMMultiMasterDetailDcmtsForm.d.ts +40 -0
  46. package/lib/components/features/documents/TMMultiMasterDetailDcmtsForm.js +296 -0
  47. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.d.ts +363 -0
  48. package/lib/components/features/documents/TMMultiMasterDetailDcmtsUtils.js +259 -0
  49. package/lib/components/features/documents/TMRelationViewer.d.ts +14 -1
  50. package/lib/components/features/documents/TMRelationViewer.js +69 -20
  51. package/lib/components/features/search/TMSearch.js +29 -16
  52. package/lib/components/features/search/TMSearchQueryEditor.js +5 -3
  53. package/lib/components/features/search/TMSearchQueryPanel.d.ts +1 -1
  54. package/lib/components/features/search/TMSearchQueryPanel.js +28 -7
  55. package/lib/components/features/search/TMSearchResult.d.ts +9 -0
  56. package/lib/components/features/search/TMSearchResult.js +419 -108
  57. package/lib/components/features/search/TMTreeSelector.js +18 -5
  58. package/lib/components/features/workflow/TMWorkflowPopup.d.ts +10 -0
  59. package/lib/components/features/workflow/TMWorkflowPopup.js +8 -4
  60. package/lib/components/layout/panelManager/TMPanelManagerContainer.d.ts +1 -0
  61. package/lib/components/layout/panelManager/TMPanelManagerContainer.js +24 -14
  62. package/lib/components/layout/panelManager/TMPanelManagerContext.js +68 -93
  63. package/lib/components/layout/panelManager/TMPanelWrapper.js +2 -2
  64. package/lib/components/layout/panelManager/types.d.ts +3 -0
  65. package/lib/components/layout/panelManager/utils.js +13 -11
  66. package/lib/components/viewers/TMMidViewer.js +4 -1
  67. package/lib/components/viewers/TMTidViewer.js +25 -11
  68. package/lib/helper/SDKUI_Globals.d.ts +82 -0
  69. package/lib/helper/SDKUI_Globals.js +119 -0
  70. package/lib/helper/SDKUI_Localizator.d.ts +24 -0
  71. package/lib/helper/SDKUI_Localizator.js +240 -0
  72. package/lib/helper/TMIcons.d.ts +1 -0
  73. package/lib/helper/TMIcons.js +3 -0
  74. package/lib/helper/dataGridSearchHelper.d.ts +28 -0
  75. package/lib/helper/dataGridSearchHelper.js +51 -0
  76. package/lib/helper/dcmtsHelper.d.ts +20 -0
  77. package/lib/helper/dcmtsHelper.js +58 -1
  78. package/lib/helper/helpers.d.ts +20 -0
  79. package/lib/helper/helpers.js +55 -1
  80. package/lib/helper/index.d.ts +1 -0
  81. package/lib/helper/index.js +1 -0
  82. package/lib/helper/queryHelper.d.ts +2 -0
  83. package/lib/helper/queryHelper.js +3 -1
  84. package/lib/hooks/tmDistinctValuesGridHelper.d.ts +16 -0
  85. package/lib/hooks/tmDistinctValuesGridHelper.js +31 -0
  86. package/lib/hooks/useArchiveListForm.d.ts +39 -0
  87. package/lib/hooks/useArchiveListForm.js +46 -0
  88. package/lib/hooks/useCaseFlowApprove.d.ts +56 -0
  89. package/lib/hooks/useCaseFlowApprove.js +157 -0
  90. package/lib/hooks/useDataUserIdItem.d.ts +1 -0
  91. package/lib/hooks/useDataUserIdItem.js +1 -0
  92. package/lib/hooks/useDocumentOperations.d.ts +2 -0
  93. package/lib/hooks/useDocumentOperations.js +28 -5
  94. package/lib/hooks/useMultiMasterDetailDcmts.d.ts +100 -0
  95. package/lib/hooks/useMultiMasterDetailDcmts.js +587 -0
  96. package/lib/hooks/useRelatedDocuments.js +2 -26
  97. package/lib/hooks/useTMDistinctValuesMetadataDisplay.d.ts +18 -0
  98. package/lib/hooks/useTMDistinctValuesMetadataDisplay.js +103 -0
  99. package/lib/hooks/useTMDistinctValuesQuickSearch.d.ts +49 -0
  100. package/lib/hooks/useTMDistinctValuesQuickSearch.js +307 -0
  101. package/lib/hooks/useTMDistinctValuesSelection.d.ts +42 -0
  102. package/lib/hooks/useTMDistinctValuesSelection.js +103 -0
  103. package/lib/hooks/useTMDistinctValuesSource.d.ts +29 -0
  104. package/lib/hooks/useTMDistinctValuesSource.js +105 -0
  105. package/lib/index.d.ts +1 -0
  106. package/lib/index.js +1 -0
  107. package/lib/services/caseflow/CaseFlowService.d.ts +236 -0
  108. package/lib/services/caseflow/CaseFlowService.js +693 -0
  109. package/lib/services/caseflow/CaseFlowTemplateCacheService.d.ts +28 -0
  110. package/lib/services/caseflow/CaseFlowTemplateCacheService.js +121 -0
  111. package/lib/services/caseflow/caseFlowNameCache.d.ts +32 -0
  112. package/lib/services/caseflow/caseFlowNameCache.js +116 -0
  113. package/lib/services/caseflow/caseFlowWorkItemUtils.d.ts +61 -0
  114. package/lib/services/caseflow/caseFlowWorkItemUtils.js +113 -0
  115. package/lib/services/index.d.ts +4 -0
  116. package/lib/services/index.js +4 -0
  117. package/lib/services/platform_services.d.ts +1 -1
  118. package/package.json +66 -66
@@ -0,0 +1,296 @@
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, getMultiDetailPanelStates, hasMultiDetailPanelStates, hoverStyle, IconArchive, IconDelete, IconDuplicate, IconEdit, IconEraser, IconInfo, IconMenuVertical, IconRefresh, IconSearch, IconSearchCheck, 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
+ loader: { display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center' },
30
+ summaryContainer: { display: 'flex', flexDirection: 'column', flex: 1, padding: '8px 12px 10px', minHeight: 0, gap: '8px' },
31
+ // I pannelli ripartiscono l'altezza in percentuale: serve un'altezza definita che non dipenda dal
32
+ // contenuto, da qui il riempimento in posizione assoluta dello spazio residuo
33
+ panelsHost: { position: 'relative', flex: 1, minHeight: 0, minWidth: 0 },
34
+ panelsHostFill: { position: 'absolute', top: 0, right: 0, bottom: 0, left: 0, display: 'flex' },
35
+ gridWrapper: { position: 'relative', flex: 1, minHeight: 0 },
36
+ actionsCell: { display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' },
37
+ actionIcon: { cursor: 'pointer', display: 'flex' },
38
+ fileCell: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
39
+ 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' },
40
+ footer: { display: 'flex', flexDirection: 'row', alignItems: 'center', justifyContent: 'center', flexWrap: 'wrap', gap: '14px', paddingTop: '8px', borderTop: `1px solid ${TMColors.card_background}`, flexShrink: 0 },
41
+ 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' },
42
+ 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' },
43
+ checkbox: { width: '16px', height: '16px', accentColor: TMColors.primary, cursor: 'pointer', margin: 0 },
44
+ };
45
+ /** Sezione mostrata per prima su mobile, dove i pannelli si vedono uno alla volta */
46
+ const MOBILE_SECTION = 'archiveList';
47
+ const TMMultiMasterDetailDcmtsForm = (props) => {
48
+ const { dtd, masterDcmt, onClose, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showDcmtFormSidebar, showTodoDcmtForm, openFileUploaderPdfEditor, graphometricManager, editPdfForm, openS4TViewer, showToppyDraggableHelpCenter, toppyHelpCenterUsePortal, fetchRemoteCertificates, onOpenS4TViewerRequest, onOpenPdfEditorRequest, onReferenceClick, onTaskCreateRequest, onRefreshAfterAddDcmtToFavs, onFileOpened, passToArchiveCallback, openWGsCopyMoveForm } = props;
49
+ const { isLoading, isSummaryReady, phase, setPhase, detailRelationItems, selectedRelation, selectedItem, hasMultipleTypes, selectRelation, backToTypeSelection, isBackLocked, closeModal, sectionsOpen, setSectionOpen, openAllSections, clearLayout, layoutResetToken, hasSavedLayout, setHasSavedLayout, canUpdateMaster, masterFieldsCount, getMasterMetadataAsync, masterInfoReloadToken, isOpenMasterForm, setIsOpenMasterForm, handleMasterSavedAsync, archivedDetailResults, archivedDetailsCount, isLoadingArchivedDetails, refreshArchivedDetailsAsync, detailColumns, detailSupportsFile, archiveRows, currentRows, focusedRowKey, setFocusedRowKey, selectedRowKeys, setSelectedRowKeys, isOpenAddForm, editingRowId, editingRow, formInputMids, openAddForm, openEditForm, closeAddForm, deleteRow, duplicateRow, upsertRow, updateRowValues, stopOnFirstError, setStopOnFirstError, isArchiving, archiveAllAsync, waitPanel, abortController, } = useMultiMasterDetailDcmts({ dtd, masterDcmt, onClose });
50
+ const deviceType = useDeviceType();
51
+ // Su mobile il panel manager mostra un pannello alla volta: le sezioni si raggiungono dalla sua barra
52
+ const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
53
+ // Un pannello alla volta non lascia spazio da cedere: le sezioni chiuse nella vista impilata si
54
+ // aprono qui, altrimenti il pannello a schermo mostrerebbe la sola testata
55
+ useEffect(() => {
56
+ if (isMobile && isSummaryReady)
57
+ openAllSections();
58
+ }, [isMobile, isSummaryReady]);
59
+ // Le altezze sono una scelta dell'utente solo da quando sposta un separatore: quelle calcolate all'apertura
60
+ // o al collasso di una sezione non vanno salvate. Con un layout già salvato lo sono comunque
61
+ const hasUserResizedRef = useRef(false);
62
+ useEffect(() => { hasUserResizedRef.current = hasMultiDetailPanelStates(); }, [layoutResetToken]);
63
+ const persistPanelStatesOnUserResize = useCallback((panelStates) => {
64
+ if (!hasUserResizedRef.current)
65
+ return;
66
+ saveMultiDetailPanelStates(panelStates);
67
+ setHasSavedLayout(true);
68
+ }, []);
69
+ // Ricerca nella griglia dei dettagli da archiviare, come in TMSearch (vedi useDocumentOperations)
70
+ const [showGridSearch, setShowGridSearch] = useState(false);
71
+ const toggleGridSearchItem = {
72
+ name: showGridSearch ? SDKUI_Localizator.HideSearch : SDKUI_Localizator.ShowSearch,
73
+ icon: _jsx(IconSearch, { fontSize: 18 }),
74
+ beginGroup: true,
75
+ onClick: () => setShowGridSearch(prev => !prev),
76
+ };
77
+ const isSummaryPhase = phase === MultiDetailArchivePhase.ArchiveSummary;
78
+ const baseTitle = SDKUI_Localizator.ArchiveMultipleDetailDocuments;
79
+ // Con un solo tipo il nome è mostrato direttamente nel titolo del modale
80
+ const modalTitle = !hasMultipleTypes && selectedItem ? `${baseTitle}: ${getDtdDisplayName(selectedItem.detailDtd)}` : baseTitle;
81
+ const isArchiveDisabled = isArchiving || currentRows.length === 0;
82
+ const archiveButtonCaption = currentRows.length > 0
83
+ ? `${SDKUI_Localizator.Archive} ${currentRows.length} ${currentRows.length === 1 ? 'dettaglio' : 'dettagli'}`
84
+ : SDKUI_Localizator.Archive;
85
+ const steps = [
86
+ ...(hasMultipleTypes ? [{ phase: MultiDetailArchivePhase.SelectDcmtType, label: SDKUI_Localizator.DcmtType }] : []),
87
+ {
88
+ phase: MultiDetailArchivePhase.ArchiveSummary,
89
+ // Nel riepilogo lo step mostra il tipo selezionato (icona + nome)
90
+ label: selectedItem
91
+ ? (_jsxs("span", { style: multiDetailStyles.stepTypeLabel, children: [_jsx(TMDcmtTypeIcon, { dtd: selectedItem.detailDtd }), _jsx("span", { style: multiDetailStyles.stepTypeLabelText, children: getDtdDisplayName(selectedItem.detailDtd) })] }))
92
+ : SDKUI_Localizator.Summary,
93
+ },
94
+ ];
95
+ // Colonne della griglia: icona del documento, metadati del tipo, file e azioni
96
+ const gridColumns = useMemo(() => {
97
+ const iconColumn = {
98
+ caption: '', width: 50, alignment: 'center',
99
+ allowSorting: false, allowFiltering: false, allowResizing: false, allowReordering: false, allowHiding: false, allowEditing: false,
100
+ cellRender: (cell) => {
101
+ const file = cell.data?.__file;
102
+ return _jsx(TMDcmtIcon, { fileExtension: file ? getFullFileExtension(file.name) : undefined, fileCount: file ? 1 : 0, tooltipContent: file?.name });
103
+ },
104
+ };
105
+ const fileColumn = {
106
+ caption: 'File', width: 220, allowSorting: false, allowEditing: false,
107
+ cellRender: (cell) => {
108
+ const file = cell.data?.__file;
109
+ return _jsx("span", { style: { ...styles.fileCell, color: file ? TMColors.text_normal : TMColors.label_normal }, children: file ? file.name : '' });
110
+ },
111
+ };
112
+ const actionsColumn = {
113
+ caption: '', width: 110, alignment: 'center',
114
+ allowSorting: false, allowFiltering: false, allowHeaderFiltering: false, allowResizing: false,
115
+ allowReordering: false, allowHiding: false, showInColumnChooser: false, allowEditing: false,
116
+ cssClass: 'tm-row-actions',
117
+ cellRender: (cell) => (_jsxs("div", { style: styles.actionsCell, children: [_jsx(TMTooltip, { content: SDKUI_Localizator.Update, children: _jsx("span", { style: styles.actionIcon, onClick: () => openEditForm(cell.data), children: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Duplicate, children: _jsx("span", { style: styles.actionIcon, onClick: () => duplicateRow(cell.data), children: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }) }) }), _jsx(TMTooltip, { content: SDKUI_Localizator.Deletion, children: _jsx("span", { style: styles.actionIcon, onClick: () => deleteRow(cell.data), children: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }) }) })] })),
118
+ };
119
+ return [actionsColumn, iconColumn, ...detailColumns, ...(detailSupportsFile ? [fileColumn] : [])];
120
+ }, [detailColumns, detailSupportsFile]);
121
+ // ----- Griglia dei dettagli: opzioni e callback con identità stabile -------------------------
122
+ // Un nuovo riferimento a ogni render farebbe rifare a DevExtreme il setup di selezione/editing
123
+ const gridSelection = useMemo(() => ({ mode: 'single', showCheckBoxesMode: 'none' }), []);
124
+ const gridEditing = useMemo(() => ({
125
+ mode: 'cell',
126
+ allowUpdating: true,
127
+ allowAdding: false,
128
+ allowDeleting: false,
129
+ startEditAction: 'click',
130
+ selectTextOnEditStart: false,
131
+ }), []);
132
+ const handleSelectionChanged = useCallback((e) => {
133
+ setSelectedRowKeys(e.selectedRowKeys ?? []);
134
+ }, [setSelectedRowKeys]);
135
+ const handleFocusedRowChanged = useCallback((e) => {
136
+ setFocusedRowKey(e.row?.key);
137
+ }, [setFocusedRowKey]);
138
+ const handleRowUpdating = useCallback((e) => {
139
+ updateRowValues(e.key, e.newData);
140
+ }, [updateRowValues]);
141
+ // Il doppio clic apre il form della riga, tranne su azioni e celle modificabili in linea
142
+ const handleCellDblClick = useCallback((e) => {
143
+ if (e.rowType !== 'data')
144
+ return;
145
+ const cssClass = e.column?.cssClass ?? '';
146
+ if (cssClass === 'tm-row-actions' || cssClass === INLINE_EDITABLE_CELL_CLASS)
147
+ return;
148
+ openEditForm(e.data);
149
+ }, [openEditForm]);
150
+ // Menu contestuale con le stesse azioni della colonna
151
+ const contextMenuItems = useMemo(() => [
152
+ { name: SDKUI_Localizator.Update, icon: _jsx(IconEdit, { fontSize: 18, color: TMColors.primary }), operationType: 'singleRow', onClick: (id) => openEditForm(archiveRows.find(r => r.id === id)) },
153
+ { name: SDKUI_Localizator.Duplicate, icon: _jsx(IconDuplicate, { fontSize: 18, color: TMColors.tertiary }), operationType: 'singleRow', onClick: (id) => duplicateRow(archiveRows.find(r => r.id === id)) },
154
+ { name: SDKUI_Localizator.Deletion, icon: _jsx(IconDelete, { fontSize: 18, color: TMColors.error }), operationType: 'singleRow', beginGroup: true, onClick: (id) => deleteRow(archiveRows.find(r => r.id === id)) },
155
+ toggleGridSearchItem,
156
+ ], [archiveRows, showGridSearch]);
157
+ // Menu delle opzioni del modale, aperto dal pulsante a tre punti nella barra del titolo.
158
+ // Con il layout di default non c'è niente da cancellare, quindi la voce non compare
159
+ const optionsMenuItems = useMemo(() => hasSavedLayout
160
+ ? [{ name: 'Cancella layout', icon: _jsx(IconEraser, { fontSize: 18, color: TMColors.error }), onClick: clearLayout }]
161
+ : [], [clearLayout, hasSavedLayout]);
162
+ // Le opzioni riguardano i pannelli del riepilogo: fuori da quella fase non hanno effetto, e su mobile
163
+ // non c'è un layout da cancellare (i pannelli si vedono uno alla volta, a tutta altezza)
164
+ const renderOptionsMenu = () => {
165
+ // Senza voci non si mostra il pulsante, per non aprire un menu vuoto
166
+ if (!isSummaryPhase || isMobile || optionsMenuItems.length === 0)
167
+ return undefined;
168
+ 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" }) }) }) }));
169
+ };
170
+ // ----- Sezione: informazioni del master -----------------------------------------------------
171
+ const renderMasterInfo = (panel) => {
172
+ const isOpen = sectionsOpen.masterInfo;
173
+ const editMasterButton = canUpdateMaster
174
+ ? (_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 }) }) }))
175
+ : undefined;
176
+ return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
177
+ section: 'masterInfo',
178
+ isOpen,
179
+ tooltip: isOpen ? SDKUI_Localizator.HideMasterInfo : SDKUI_Localizator.ShowMasterInfo,
180
+ count: masterFieldsCount,
181
+ title: (_jsxs(_Fragment, { children: [_jsx(TMDcmtTypeIcon, { dtd: dtd }), _jsxs("span", { style: multiDetailStyles.stepTypeLabelText, children: ["Master: ", getDtdDisplayName(dtd)] })] })),
182
+ actions: editMasterButton,
183
+ panel,
184
+ onToggleOpen: setSectionOpen,
185
+ }), isOpen && (_jsx(TMMasterInfoFields, { dtd: dtd, dcmt: masterDcmt, getValuesByMidAsync: getMasterMetadataAsync, reloadToken: masterInfoReloadToken, isExpandedView: panel.isMaximized }))] }));
186
+ };
187
+ // ----- Sezione: dettagli già archiviati -----------------------------------------------------
188
+ const renderArchivedDetails = (panel) => {
189
+ const isOpen = sectionsOpen.archivedDetails;
190
+ const hasResults = archivedDetailResults.length > 0 && archivedDetailsCount > 0;
191
+ // Ricarica i dettagli già archiviati: la sezione va aperta, altrimenti l'esito non sarebbe visibile
192
+ 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 }) }) }));
193
+ return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
194
+ section: 'archivedDetails',
195
+ isOpen,
196
+ tooltip: isOpen ? SDKUI_Localizator.HideArchivedDetails : SDKUI_Localizator.ShowArchivedDetails,
197
+ count: archivedDetailsCount,
198
+ title: (_jsxs(_Fragment, { children: [_jsx(IconSearchCheck, { fontSize: 15 }), _jsx("span", { style: multiDetailStyles.stepTypeLabelText, children: SDKUI_Localizator.ArchivedDetails })] })),
199
+ actions: refreshArchivedButton,
200
+ panel,
201
+ onToggleOpen: setSectionOpen,
202
+ }), isOpen && (_jsx("div", { style: { ...accordionStyles.accordionListBody, ...(panel.isMaximized ? { borderTop: 'none' } : {}) }, children: isLoadingArchivedDetails
203
+ ? _jsxs("div", { style: accordionStyles.accordionListPlaceholder, children: [_jsx(LoadIndicator, { height: 30, width: 30 }), SDKUI_Localizator.Loading] })
204
+ : (hasResults
205
+ ? _jsx(TMSearchResult, { showToolbarHeader: false, showCounterConfig: false, showActionsColumn: true, compactPager: !panel.isMaximized, context: SearchResultContext.METADATA_SEARCH, searchResults: archivedDetailResults, onClose: () => setSectionOpen('archivedDetails', false), showBackButton: false, showToolbarCloseButton: false, allowFloatingBar: false, allowRelations: false, openDcmtFormAsModal: true, showSearchResultSidebar: false, showDcmtFormSidebar: true, autoFocusFirstRow: false, onRefreshSearchAsyncDatagrid: refreshArchivedDetailsAsync, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, showTodoDcmtForm: showTodoDcmtForm, openFileUploaderPdfEditor: openFileUploaderPdfEditor, graphometricManager: graphometricManager, editPdfForm: editPdfForm, openS4TViewer: openS4TViewer, showToppyDraggableHelpCenter: showToppyDraggableHelpCenter, toppyHelpCenterUsePortal: toppyHelpCenterUsePortal, fetchRemoteCertificates: fetchRemoteCertificates, onOpenS4TViewerRequest: onOpenS4TViewerRequest, onOpenPdfEditorRequest: onOpenPdfEditorRequest, onReferenceClick: onReferenceClick, onTaskCreateRequest: onTaskCreateRequest, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onFileOpened: onFileOpened, passToArchiveCallback: passToArchiveCallback, openWGsCopyMoveForm: openWGsCopyMoveForm })
206
+ : _jsx("div", { style: accordionStyles.accordionListPlaceholder, children: SDKUI_Localizator.NoDataToDisplay })) }))] }));
207
+ };
208
+ // ----- Sezione: dettagli da archiviare ------------------------------------------------------
209
+ const renderArchiveList = (panel) => {
210
+ const isOpen = sectionsOpen.archiveList;
211
+ return (_jsxs("div", { style: { ...accordionStyles.accordion, ...accordionStyles.accordionInPanel }, children: [renderAccordionHeader({
212
+ section: 'archiveList',
213
+ isOpen,
214
+ tooltip: isOpen ? SDKUI_Localizator.HideDetailsToArchive : SDKUI_Localizator.ShowDetailsToArchive,
215
+ count: currentRows.length,
216
+ title: (_jsxs(_Fragment, { children: [_jsx(IconArchive, { fontSize: 15 }), _jsx("span", { style: multiDetailStyles.stepTypeLabelText, children: SDKUI_Localizator.DetailsToArchive })] })),
217
+ panel,
218
+ onToggleOpen: setSectionOpen,
219
+ }), 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' } }) }) })] }))] }));
220
+ };
221
+ // ----- Pannelli del riepilogo, uno per sezione ----------------------------------------------
222
+ const renderSectionsInPanels = () => {
223
+ const definitions = [
224
+ { section: 'masterInfo', name: `Master: ${getDtdDisplayName(dtd)}`, icon: _jsx(IconInfo, { fontSize: 22 }), count: masterFieldsCount, render: renderMasterInfo },
225
+ ...(selectedRelation?.detailTID !== undefined
226
+ ? [{ section: 'archivedDetails', name: SDKUI_Localizator.ArchivedDetails, icon: _jsx(IconSearchCheck, { fontSize: 22 }), count: archivedDetailsCount, render: renderArchivedDetails }]
227
+ : []),
228
+ { section: 'archiveList', name: SDKUI_Localizator.DetailsToArchive, icon: _jsx(IconArchive, { fontSize: 22 }), count: currentRows.length, render: renderArchiveList },
229
+ ];
230
+ const panels = definitions.map((definition, index) => ({
231
+ id: definition.section,
232
+ name: definition.name,
233
+ currentGroupDirection: 'vertical',
234
+ contentOptions: {
235
+ // Ingrandire una sezione collassata la apre: altrimenti il pannello a tutto schermo sarebbe vuoto
236
+ component: _jsx(TMSummarySectionSlot, { section: definition.section, render: definition.render, onBeforeMaximize: (section) => setSectionOpen(section, true) }),
237
+ // Una sezione chiusa vale 0% di altezza: è questo minimo a tenerne a schermo la testata
238
+ minHeight: `${SECTION_HEADER_HEIGHT_PX}px`,
239
+ // La sezione collassata è alta quanto la sua testata: i separatori non la ingrandiscono
240
+ isCollapsed: !sectionsOpen[definition.section],
241
+ },
242
+ // Barra dei pannelli: su mobile è l'unico modo per passare da una sezione all'altra
243
+ toolbarOptions: { icon: definition.icon, visible: true, isActive: true, orderNumber: index + 1, count: definition.count, tooltip: definition.name },
244
+ }));
245
+ const sections = panels.map(panel => panel.id);
246
+ // Le altezze salvate sono il punto di partenza, ma è il collasso corrente a decidere le quote
247
+ const savedStates = getMultiDetailPanelStates();
248
+ const savedDimensions = hasMultiDetailPanelStates()
249
+ ? sections.reduce((acc, section) => {
250
+ if (savedStates[section])
251
+ acc[section] = { width: savedStates[section].width, height: savedStates[section].height };
252
+ return acc;
253
+ }, {})
254
+ : undefined;
255
+ // Con un pannello alla volta non ci sono quote da spartire: quello a schermo prende tutta l'altezza.
256
+ // Le quote proporzionali (e il layout salvato) valgono solo per i pannelli impilati
257
+ const fullHeights = sections.reduce((acc, section) => { acc[section] = { width: '100%', height: '100%' }; return acc; }, {});
258
+ const initialDimensions = isMobile ? fullHeights : sectionPanelHeights(sections, sectionsOpen, savedDimensions);
259
+ const initialVisibility = sections.reduce((acc, section) => { acc[section] = isMobile ? section === MOBILE_SECTION : true; return acc; }, {});
260
+ return (_jsx("div", { style: styles.panelsHost, children: _jsx("div", { style: styles.panelsHostFill, children: _jsxs(TMPanelManagerWithPersistenceProvider
261
+ // Le dimensioni sono lette solo al montaggio: dopo la cancellazione del layout, e al
262
+ // passaggio fra impilato e un pannello alla volta, i pannelli vanno rimontati
263
+ , { panels: panels, initialVisibility: initialVisibility, defaultDimensions: initialDimensions, initialDimensions: initialDimensions, initialMobilePanelId: MOBILE_SECTION,
264
+ // Su mobile il layout è imposto dal dispositivo: salvarlo sovrascriverebbe quello dell'utente
265
+ isPersistenceEnabled: !isMobile,
266
+ // Le altezze si salvano solo dopo che l'utente ha spostato un separatore (vedi TMSummaryResizeTracker)
267
+ 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}`) }) }));
268
+ };
269
+ 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: {
270
+ ...styles.archiveButton,
271
+ background: isArchiveDisabled ? TMColors.disabled : TMColors.success,
272
+ boxShadow: isArchiveDisabled ? 'none' : `0 4px 14px ${TMColors.success}55`,
273
+ cursor: isArchiveDisabled ? 'default' : 'pointer',
274
+ opacity: isArchiveDisabled ? 0.6 : 1,
275
+ }, ...(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] })] })] }));
276
+ // ----- Render principale --------------------------------------------------------------------
277
+ if (isLoading)
278
+ return null;
279
+ return _jsxs(_Fragment, { children: [_jsx(TMLayoutWaitingContainer, { direction: 'vertical', ...waitPanel, isCancelable: true, abortController: abortController, usePortal: true, children: _jsx(TMModal, { width: isSummaryPhase ? '95%' : '720px', height: isSummaryPhase ? '95%' : 'auto', onClose: closeModal, title: modalTitle, hidePopup: false, askClosingConfirm: archiveRows.length > 0, recenterOnResize: true, titleActions: renderOptionsMenu(), children: _jsxs("div", { style: styles.content, children: [hasMultipleTypes && _jsx("div", { style: styles.header, children: renderStepper({
280
+ steps,
281
+ phase,
282
+ hasMultipleTypes,
283
+ isBackLocked,
284
+ showBackArrow: isSummaryPhase,
285
+ onBack: backToTypeSelection,
286
+ onStepClick: (stepPhase) => (stepPhase === MultiDetailArchivePhase.SelectDcmtType ? backToTypeSelection() : setPhase(stepPhase)),
287
+ }) }), !isSummaryPhase
288
+ ? renderSelectDcmtType({ detailRelationItems, selectedRelation, onSelectRelation: selectRelation })
289
+ : (isSummaryReady
290
+ ? renderArchiveSummary()
291
+ : _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: {
292
+ isEditingRow: editingRowId !== undefined,
293
+ onUpsertRow: (values, file) => upsertRow(values, file),
294
+ }, 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 }))] });
295
+ };
296
+ export default TMMultiMasterDetailDcmtsForm;