@topconsultnpm/sdkui-react-beta 6.14.67 → 6.14.68

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.
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useEffect, useState } from "react";
3
3
  import styled from "styled-components";
4
4
  import { AccessLevels, DcmtTypeListCacheService, LayoutModes, MetadataDataDomains, MetadataDataTypes, SDK_Globals } from '@topconsultnpm/sdk-ts-beta';
5
- import { IconUndo, IconPencil, IconFunction, IconMenuVertical, IconDataList, SDKUI_Localizator, IconNull, stringIsNullOrEmpty, deepCompare, SDKUI_Globals } from "../../helper";
5
+ import { IconUndo, IconPencil, IconFunction, IconMenuVertical, IconDataList, SDKUI_Localizator, IconNull, stringIsNullOrEmpty, deepCompare, SDKUI_Globals, IconDcmtTypeSys } from "../../helper";
6
6
  import { TMColors } from "../../utils/theme";
7
7
  import TMButton from "../base/TMButton";
8
8
  import TMDropDownMenu from "../base/TMDropDownMenu";
@@ -10,6 +10,7 @@ import TMTooltip from "../base/TMTooltip";
10
10
  import TMCheckBox from "./TMCheckBox";
11
11
  import TMMetadataEditor, { useMetadataEditableList } from "./TMMetadataEditor";
12
12
  import { FormulaHelper } from "./TMFormulaEditor";
13
+ import { TMNothingToShow } from "../features/documents/TMDcmtPreview";
13
14
  export var ShowCheckBoxesMode;
14
15
  (function (ShowCheckBoxesMode) {
15
16
  ShowCheckBoxesMode[ShowCheckBoxesMode["Never"] = 0] = "Never";
@@ -67,6 +68,11 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
67
68
  }
68
69
  };
69
70
  useEffect(() => {
71
+ if (!TID) {
72
+ setCurrentDTD(undefined);
73
+ setDynDataListsToBeRefreshed([]);
74
+ return;
75
+ }
70
76
  // Passiamo did = undefined, perché è già in cache
71
77
  DcmtTypeListCacheService.GetWithNotGrantedAsync(TID, undefined).then((resultDTD) => {
72
78
  setCurrentDTD(resultDTD);
@@ -206,60 +212,62 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
206
212
  ];
207
213
  return menu;
208
214
  };
209
- return (_jsx(StyledMetadataValuesContainer, { children: metadataValues.map((item) => (_jsxs(StyledRow, { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '6px' : '0', gap: '8px' }, onClick: () => { handleMetadataValueSelection(item); }, onFocus: () => { handleMetadataValueSelection(item); }, children: [showCheckBoxes !== ShowCheckBoxesMode.Never &&
210
- _jsx(TMCheckBox, { elementStyle: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '14px' : '20px' }, value: item.isSelected, disabled: showCheckBoxes === ShowCheckBoxesMode.AlwaysReadOnly, onValueChanged: (newValue) => {
211
- let newValues = structuredClone(metadataValues);
212
- const mvd = newValues.find(value => value.mid === item.mid);
213
- if (mvd)
214
- mvd.isSelected = newValue;
215
- onValueChanged?.(newValues);
216
- } }), _jsxs("div", { style: { position: 'relative', height: '100%', width: '100%', opacity: showNullValueCheckBoxes && item.isNull ? 0.4 : 1 }, children: [_jsx(TMMetadataEditor, { tid: TID, mid: item.mid, layoutMode: layoutMode, isLexProt: item.isLexProt, isSelected: isOpenDistinctValues && item.mid === selectedMID, isModifiedWhen: (item.value ?? '') !== (metadataValuesOrig.find(m => m.mid === item.mid)?.value ?? ''), isReadOnly: showNullValueCheckBoxes ? item.isNull : undefined, isEditable: item.isEditable, validationItems: editorValidationHandler(item.mid ?? 0), value: item.value, openChooserBySingleClick: openChooserBySingleClick, onValueChanged: (newValue) => { onChangeHandler(newValue, item.mid ?? 0); }, onValueChange: (newValue) => { onChangeHandler(newValue, item.mid ?? 0); }, queryParamsDynDataList: dynDataListsToBeRefreshed.find(o => o.mid == item.mid)?.queryParams ?? [], onCascadeRefreshDynDataLists: (ddlToBeRefreshed) => {
217
- let newDynDataListsToBeRefreshed = [];
218
- for (const item of dynDataListsToBeRefreshed) {
219
- let index = ddlToBeRefreshed.findIndex(o => o.mid == item.mid || (o.mid == -1 && o.midStarter == item.midStarter));
220
- if (index >= 0)
221
- continue;
222
- newDynDataListsToBeRefreshed.push(item);
223
- }
224
- for (const item of ddlToBeRefreshed) {
225
- if (item.queryParams.length <= 0)
226
- continue;
227
- if (!item.mid)
228
- continue;
229
- if (item.mid <= 0)
230
- continue;
231
- newDynDataListsToBeRefreshed.push(item);
232
- }
233
- setDynDataListsToBeRefreshed(newDynDataListsToBeRefreshed);
234
- }, onCascadeUpdateMIDs: (midsToBeUpdated) => {
235
- //Attenzione. Il primo elemento di midsToBeUpdated è il currentMID con il new value.
236
- //nonostante passi prima da onValueChanged, la collection metadata non risulta aggiornata!
237
- let newMetadata = structuredClone(metadataValues);
238
- for (const item of midsToBeUpdated) {
239
- const mdItem = newMetadata.find(value => value.mid === item.mid);
240
- if (mdItem)
241
- mdItem.value = item.value;
242
- }
243
- onValueChanged?.(newMetadata);
244
- } }), FormulaHelper.isFormula(item.value)
245
- ? _jsx(IconFunction, { color: "#1a89d3", fontSize: 14, style: { position: "absolute", top: item.md?.dataType === MetadataDataTypes.DateTime ? '3px' : '5px', left: '14px' } })
246
- : (isEditable(item.mid) || item.isEditable)
247
- ? _jsx(IconPencil, { color: "#138603", fontSize: 14, style: { position: "absolute", top: item.md?.dataType === MetadataDataTypes.DateTime ? '3px' : '5px', left: '16px' } })
248
- : _jsx(_Fragment, {})] }), showNullValueCheckBoxes &&
249
- _jsx(TMTooltip, { content: "Imposta <Null>", children: _jsx(IconNull, { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '12px' : '18px', opacity: item.md?.isRequired === 1 ? 0.4 : 1 }, cursor: item.md?.isRequired === 1 ? 'default' : 'pointer', color: item.isNull ? TMColors.isModified : TMColors.button_icon, onClick: () => {
250
- if (item.md?.isRequired === 1)
251
- return;
252
- let newValues = structuredClone(metadataValues);
253
- const mvd = newValues.find(value => value.mid === item.mid);
254
- if (mvd) {
255
- mvd.isNull = !mvd.isNull;
256
- if (mvd.isNull)
257
- mvd.isSelected = true;
258
- else
259
- mvd.isSelected = !stringIsNullOrEmpty(mvd.value);
260
- }
261
- onValueChanged?.(newValues);
262
- } }) }), !isReadOnly && _jsx("div", { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '12px' : '18px' }, onClick: () => { handleMetadataValueSelection(item); }, children: _jsx(TMDropDownMenu, { backgroundColor: 'white', color: TMColors.button_icon, borderRadius: '3px', content: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconMenuVertical, {}), showTooltip: false }), disabled: item.isLexProt === 1, items: getAdvancedMenuItems(item) }) })] }, item.mid))) }));
215
+ return (_jsx(StyledMetadataValuesContainer, { children: !TID ?
216
+ _jsx(TMNothingToShow, { text: 'Nessun documento selezionato.', secondText: 'Metadati di sistema non disponibile.', icon: _jsx(IconDcmtTypeSys, { fontSize: 96 }) }) :
217
+ _jsx(_Fragment, { children: metadataValues.map((item) => (_jsxs(StyledRow, { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '6px' : '0', gap: '8px' }, onClick: () => { handleMetadataValueSelection(item); }, onFocus: () => { handleMetadataValueSelection(item); }, children: [showCheckBoxes !== ShowCheckBoxesMode.Never &&
218
+ _jsx(TMCheckBox, { elementStyle: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '14px' : '20px' }, value: item.isSelected, disabled: showCheckBoxes === ShowCheckBoxesMode.AlwaysReadOnly, onValueChanged: (newValue) => {
219
+ let newValues = structuredClone(metadataValues);
220
+ const mvd = newValues.find(value => value.mid === item.mid);
221
+ if (mvd)
222
+ mvd.isSelected = newValue;
223
+ onValueChanged?.(newValues);
224
+ } }), _jsxs("div", { style: { position: 'relative', height: '100%', width: '100%', opacity: showNullValueCheckBoxes && item.isNull ? 0.4 : 1 }, children: [_jsx(TMMetadataEditor, { tid: TID, mid: item.mid, layoutMode: layoutMode, isLexProt: item.isLexProt, isSelected: isOpenDistinctValues && item.mid === selectedMID, isModifiedWhen: (item.value ?? '') !== (metadataValuesOrig.find(m => m.mid === item.mid)?.value ?? ''), isReadOnly: showNullValueCheckBoxes ? item.isNull : undefined, isEditable: item.isEditable, validationItems: editorValidationHandler(item.mid ?? 0), value: item.value, openChooserBySingleClick: openChooserBySingleClick, onValueChanged: (newValue) => { onChangeHandler(newValue, item.mid ?? 0); }, onValueChange: (newValue) => { onChangeHandler(newValue, item.mid ?? 0); }, queryParamsDynDataList: dynDataListsToBeRefreshed.find(o => o.mid == item.mid)?.queryParams ?? [], onCascadeRefreshDynDataLists: (ddlToBeRefreshed) => {
225
+ let newDynDataListsToBeRefreshed = [];
226
+ for (const item of dynDataListsToBeRefreshed) {
227
+ let index = ddlToBeRefreshed.findIndex(o => o.mid == item.mid || (o.mid == -1 && o.midStarter == item.midStarter));
228
+ if (index >= 0)
229
+ continue;
230
+ newDynDataListsToBeRefreshed.push(item);
231
+ }
232
+ for (const item of ddlToBeRefreshed) {
233
+ if (item.queryParams.length <= 0)
234
+ continue;
235
+ if (!item.mid)
236
+ continue;
237
+ if (item.mid <= 0)
238
+ continue;
239
+ newDynDataListsToBeRefreshed.push(item);
240
+ }
241
+ setDynDataListsToBeRefreshed(newDynDataListsToBeRefreshed);
242
+ }, onCascadeUpdateMIDs: (midsToBeUpdated) => {
243
+ //Attenzione. Il primo elemento di midsToBeUpdated è il currentMID con il new value.
244
+ //nonostante passi prima da onValueChanged, la collection metadata non risulta aggiornata!
245
+ let newMetadata = structuredClone(metadataValues);
246
+ for (const item of midsToBeUpdated) {
247
+ const mdItem = newMetadata.find(value => value.mid === item.mid);
248
+ if (mdItem)
249
+ mdItem.value = item.value;
250
+ }
251
+ onValueChanged?.(newMetadata);
252
+ } }), FormulaHelper.isFormula(item.value)
253
+ ? _jsx(IconFunction, { color: "#1a89d3", fontSize: 14, style: { position: "absolute", top: item.md?.dataType === MetadataDataTypes.DateTime ? '3px' : '5px', left: '14px' } })
254
+ : (isEditable(item.mid) || item.isEditable)
255
+ ? _jsx(IconPencil, { color: "#138603", fontSize: 14, style: { position: "absolute", top: item.md?.dataType === MetadataDataTypes.DateTime ? '3px' : '5px', left: '16px' } })
256
+ : _jsx(_Fragment, {})] }), showNullValueCheckBoxes &&
257
+ _jsx(TMTooltip, { content: "Imposta <Null>", children: _jsx(IconNull, { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '12px' : '18px', opacity: item.md?.isRequired === 1 ? 0.4 : 1 }, cursor: item.md?.isRequired === 1 ? 'default' : 'pointer', color: item.isNull ? TMColors.isModified : TMColors.button_icon, onClick: () => {
258
+ if (item.md?.isRequired === 1)
259
+ return;
260
+ let newValues = structuredClone(metadataValues);
261
+ const mvd = newValues.find(value => value.mid === item.mid);
262
+ if (mvd) {
263
+ mvd.isNull = !mvd.isNull;
264
+ if (mvd.isNull)
265
+ mvd.isSelected = true;
266
+ else
267
+ mvd.isSelected = !stringIsNullOrEmpty(mvd.value);
268
+ }
269
+ onValueChanged?.(newValues);
270
+ } }) }), !isReadOnly && _jsx("div", { style: { marginTop: item.md?.dataType === MetadataDataTypes.DateTime ? '12px' : '18px' }, onClick: () => { handleMetadataValueSelection(item); }, children: _jsx(TMDropDownMenu, { backgroundColor: 'white', color: TMColors.button_icon, borderRadius: '3px', content: _jsx(TMButton, { btnStyle: 'icon', icon: _jsx(IconMenuVertical, {}), showTooltip: false }), disabled: item.isLexProt === 1, items: getAdvancedMenuItems(item) }) })] }, item.mid))) }) }));
263
271
  };
264
272
  export default TMMetadataValues;
265
273
  //#region Styled Components
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useEffect, useMemo, useState } from 'react';
3
3
  import styled, { keyframes } from 'styled-components';
4
4
  import TMDcmtPreview from './TMDcmtPreview';
5
- import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutModes, MetadataDataTypes, ResultTypes, SDK_Globals, SDK_Localizator, SystemMIDsAsNumber, TemplateTIDs, UpdateEngineByID, ValidationItem } from '@topconsultnpm/sdk-ts-beta';
5
+ import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutModes, MetadataDataTypes, ResultTypes, SDK_Globals, SDK_Localizator, SystemMIDsAsNumber, TemplateTIDs, TID_DID, UpdateEngineByID, ValidationItem } from '@topconsultnpm/sdk-ts-beta';
6
6
  import { ContextMenu } from 'devextreme-react';
7
7
  import { WorkFlowApproveRejectPopUp, WorkFlowOperationButtons, WorkFlowReAssignPopUp } from '../workflow/TMWorkflowPopup';
8
8
  import { DownloadTypes, FormModes } from '../../../ts';
@@ -32,9 +32,10 @@ import toppy from '../../../assets/Toppy-generico.png';
32
32
  import { useTMPanelManagerContext } from '../../layout/panelManager/TMPanelManagerContext';
33
33
  import TMPanelManagerContainer from '../../layout/panelManager/TMPanelManagerContainer';
34
34
  import { TMPanelManagerWithPersistenceProvider } from '../../layout/panelManager/TMPanelManagerWithPersistenceProvider';
35
+ import { useWorkflowApprove } from '../../../hooks/useWorkflowApprove';
35
36
  let abortControllerLocal = new AbortController();
36
37
  //#endregion
37
- const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, onClose, onSavedAsyncCallback, TID, DID, formMode = FormModes.Update, canNext, canPrev, count, itemIndex, onNext, onPrev, allowNavigation = true, allowRelations = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, showDcmtFormSidebar = true, invokedByTodo = false, titleModal, isModal = false, widthModal = "100%", heightModal = "100%", groupId, onWFOperationCompleted, onTaskCreateRequest, fileFromConnector = null, taskFormDialogComponent }) => {
38
+ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, onClose, onSavedAsyncCallback, TID, DID, formMode = FormModes.Update, canNext, canPrev, count, itemIndex, onNext, onPrev, allowNavigation = true, allowRelations = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, showDcmtFormSidebar = true, invokedByTodo = false, titleModal, isModal = false, widthModal = "100%", heightModal = "100%", groupId, onWFOperationCompleted, onTaskCreateRequest, fileFromConnector = null, taskFormDialogComponent, }) => {
38
39
  const [id, setID] = useState('');
39
40
  const [showWaitPanelLocal, setShowWaitPanelLocal] = useState(false);
40
41
  const [waitPanelTitleLocal, setWaitPanelTitleLocal] = useState('');
@@ -66,8 +67,11 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
66
67
  const [dcmtFile, setDcmtFile] = useState(null);
67
68
  const [focusedMetadataValue, setFocusedMetadataValue] = useState();
68
69
  const [showAll, setShowAll] = useState(layoutMode === LayoutModes.Ark);
70
+ const [workItems, setWorkItems] = useState([]);
69
71
  const { openConfirmAttachmentsDialog, ConfirmAttachmentsDialog } = useInputAttachmentsDialog();
70
72
  const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync } = useDcmtOperations();
73
+ // Custom hook to manage workflow approval data
74
+ const { workflowApproveData, refreshWorkflowApprove } = useWorkflowApprove();
71
75
  const deviceType = useDeviceType();
72
76
  const getDcmts = () => { return [{ TID: currentDcmt?.tid, DID: currentDcmt?.did, FILEEXT: currentDcmt?.fileExt }]; };
73
77
  const fetchData = async () => {
@@ -180,6 +184,28 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
180
184
  return;
181
185
  setFocusedMetadataValue(undefined);
182
186
  }, [fromDTD, layoutMode]);
187
+ useEffect(() => {
188
+ if (layoutMode !== LayoutModes.Update)
189
+ return;
190
+ if (!DID) {
191
+ setWorkItems([]);
192
+ return;
193
+ }
194
+ let items = [];
195
+ // Cerchiamo tutti i workitem(S) associati al DID corrente (potrebbero esserci più istanze di workflow)
196
+ for (const workflow of workflowApproveData) {
197
+ for (const dataRow of workflow.dtdResult?.rows ?? []) {
198
+ let did = Number(dataRow?.[1]);
199
+ if (did === Number(DID)) {
200
+ let w = new TID_DID();
201
+ w.tid = Number(dataRow?.[0]);
202
+ w.did = did;
203
+ items.push(w);
204
+ }
205
+ }
206
+ }
207
+ setWorkItems(items);
208
+ }, [workflowApproveData, DID, layoutMode]);
183
209
  const fileIsNotValid = (fromDTD?.archiveConstraint === ArchiveConstraints.ContentCompulsory && !dcmtFile);
184
210
  const middlePanelToolbar = _jsxs("div", { style: { width: 'max-content', display: 'flex', alignItems: 'center', gap: '10px' }, children: [layoutMode === LayoutModes.Update && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', isModified: false, formMode: FormModes.ReadOnly, canPrev: canPrev, onPrev: () => onPrev?.() }), layoutMode === LayoutModes.Update && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', isModified: false, formMode: FormModes.ReadOnly, canNext: canNext, onNext: () => onNext?.() })] });
185
211
  const getSelectionDcmtInfo = () => {
@@ -199,6 +225,9 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
199
225
  const isSysMetadataDisabled = layoutMode !== LayoutModes.Update;
200
226
  const isDetailsDisabled = layoutMode !== LayoutModes.Update || !DID;
201
227
  const isMasterDisabled = layoutMode !== LayoutModes.Update || !DID;
228
+ const showToppyForApprove = layoutMode === LayoutModes.Update && workItems.length > 0 && !isOpenDetails && !isOpenMaster;
229
+ const approvalVID = workItems.length > 0
230
+ ? Number(workItems[0].tid) : -1;
202
231
  const commandsMenuItems = [
203
232
  { icon: svgToString(_jsx(IconDownload, {})), operationType: 'singleRow', disabled: fromDTD?.perm?.canRetrieveFile !== AccessLevels.Yes, text: "Download file", onClick: async () => await downloadDcmtsAsync(getDcmts(), DownloadTypes.Dcmt, "download") },
204
233
  { icon: svgToString(_jsx(IconDownload, {})), operationType: 'singleRow', disabled: !isXMLFileExt(currentDcmt?.fileExt), text: "Download allegati XML", onClick: async () => await downloadDcmtsAsync(getDcmts(), DownloadTypes.Attachment, "download", undefined, openConfirmAttachmentsDialog) },
@@ -591,15 +620,17 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
591
620
  isEditable: true,
592
621
  value: FormulaHelper.addFormulaTag(newFormula.expression)
593
622
  }));
594
- } }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: TID, DID: DID, isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: TID, DID: DID, isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: TID, DID: DID, onClose: () => setShowReAssignPopup(false) }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }), (fromDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isOpenDetails && !isOpenMaster && layoutMode === LayoutModes.Update) &&
595
- _jsx(ToppyHelpCenter, { deviceType: deviceType, content: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => setShowApprovePopup(true), onSignApprove: () => ShowAlert({ message: 'TODO', mode: 'info', title: SDKUI_Localizator.SignatureAndApprove, duration: 3000 }), onReject: () => { setShowRejectPopup(true); }, onReAssign: () => { setShowReAssignPopup(true); }, onMoreInfo: () => {
596
- const vid = Number(TID);
597
- const did = Number(DID);
598
- openTaskFormHandler((task) => {
599
- SDK_Globals.tmSession?.NewWorkflowEngine().WorkItem_MoreInfoAsync(vid, did, task?.id ?? 0)
600
- .catch(err => TMExceptionBoxManager.show({ exception: err }));
601
- });
602
- } }) }) }), isOpenDetails &&
623
+ } }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, onClose: () => setShowReAssignPopup(false) }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }), showToppyForApprove &&
624
+ _jsx(ToppyHelpCenter, { deviceType: deviceType, top: workItems.length === 1 ? -220 : -120, content: workItems.length === 1 ?
625
+ _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: _jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => setShowApprovePopup(true), onSignApprove: () => ShowAlert({ message: 'TODO', mode: 'info', title: SDKUI_Localizator.SignatureAndApprove, duration: 3000 }), onReject: () => { setShowRejectPopup(true); }, onReAssign: () => { setShowReAssignPopup(true); }, onMoreInfo: () => {
626
+ const did = Number(DID);
627
+ openTaskFormHandler((task) => {
628
+ SDK_Globals.tmSession?.NewWorkflowEngine().WorkItem_MoreInfoAsync(approvalVID, did, task?.id ?? 0)
629
+ .catch(err => TMExceptionBoxManager.show({ exception: err }));
630
+ });
631
+ } }) })
632
+ :
633
+ _jsxs("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: [`Devi approvare ${workItems.length} workitem(s) per questo documento.`, `Vai alla sezione di approvazione.`] }) }), isOpenDetails &&
603
634
  _jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, isForMaster: false, inputDcmts: getSelectionDcmtInfo(), allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenDetails(false) }) }), isOpenMaster &&
604
635
  _jsxs(StyledModalContainer, { style: { backgroundColor: 'white' }, children: [_jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: getSelectionDcmtInfo(), isForMaster: true, allowNavigation: allowNavigation, canNext: canNext, canPrev: canPrev, onNext: onNext, onPrev: onPrev, onBack: () => setIsOpenMaster(false), appendMasterDcmts: handleAddItem }), secondaryMasterDcmts.length > 0 && secondaryMasterDcmts.map((dcmt, index) => {
605
636
  return (_jsx(StyledModalContainer, { style: { backgroundColor: 'white' }, children: _jsx(TMMasterDetailDcmts, { deviceType: deviceType, inputDcmts: [dcmt], isForMaster: true, allowNavigation: false, onBack: () => handleRemoveItem(dcmt.TID, dcmt.DID), appendMasterDcmts: handleAddItem }) }, `${index}-${dcmt.DID}`));
@@ -663,7 +694,7 @@ const ToppyImage = styled.img `
663
694
  height: ${props => props.$isMobile ? '140px' : '200px'};
664
695
  cursor: ${props => props.$isMobile ? 'pointer' : 'default'};
665
696
  `;
666
- export const ToppyHelpCenter = ({ content, onClick, deviceType, top = -200 }) => {
697
+ export const ToppyHelpCenter = ({ content, onClick, deviceType, top = -220 }) => {
667
698
  return (_jsxs(ToppyContainer, { children: [_jsx(ToppyImage, { "$isMobile": deviceType === DeviceType.MOBILE, onClick: onClick, src: toppy, alt: "Toppy" }), _jsx("div", { style: { top: deviceType === DeviceType.MOBILE ? -80 : top, right: deviceType === DeviceType.MOBILE ? 40 : 1, transform: 'rotate(20deg)', position: 'absolute', width: 'max-content', height: 'max-content' }, children: content })] }));
668
699
  };
669
700
  const TMDcmtPreviewWrapper = ({ currentDcmt, layoutMode, fromDTD, dcmtFile, deviceType, isVisible, onFileUpload }) => {
@@ -39,6 +39,7 @@ const TMSearch = ({ inputTID, inputSqdID, isExpertMode = SDKUI_Globals.userSetti
39
39
  if (!inputTID)
40
40
  return;
41
41
  setCurrentTID(inputTID);
42
+ setCurrentSearchView(TMSearchViews.Search);
42
43
  }, [inputTID]);
43
44
  useEffect(() => {
44
45
  if (inputSqdID) {
@@ -195,10 +196,13 @@ const TMSearch = ({ inputTID, inputSqdID, isExpertMode = SDKUI_Globals.userSetti
195
196
  toolbarOptions: { icon: _jsx(IconSavedQuery, { fontSize: 24 }), visible: true, orderNumber: 4, isActive: allInitialPanelVisibility['TMSavedQuerySelector'] }
196
197
  }
197
198
  ], [tmTreeSelectorElement, tmRecentsManagerElement, tmSearchQueryPanelElement, tmSavedQuerySelectorElement, fromDTD, mruTIDs]);
198
- return (_jsxs(_Fragment, { children: [_jsx(StyledMultiViewPanel, { "$isVisible": currentSearchView === TMSearchViews.Search, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'TMRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }), searchResult.length > 0 &&
199
- _jsx(TMSearchResult, { isVisible: currentSearchView === TMSearchViews.Result, context: SearchResultContext.METADATA_SEARCH, searchResults: searchResult, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onRefreshSearchAsync: async () => {
200
- setSearchResult(await refreshLastSearch(lastQdSearched) ?? []);
201
- }, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setCurrentSearchView(TMSearchViews.Search); }, onFileOpened: onFileOpened, focusedWorkingGroupId: focusedWorkingGroupId, fetchTreeFileSystemForWorkingGroup: fetchTreeFileSystemForWorkingGroup, fetchArchivedDocumentsForWorkingGroup: fetchArchivedDocumentsForWorkingGroup })] }));
199
+ return (_jsxs(_Fragment, { children: [_jsx(StyledMultiViewPanel, { "$isVisible": currentSearchView === TMSearchViews.Search, children: _jsx(TMPanelManagerProvider, { panels: initialPanels, initialVisibility: allInitialPanelVisibility, defaultDimensions: initialPanelDimensions, initialDimensions: initialPanelDimensions, initialMobilePanelId: 'TMRecentsManager', children: _jsx(TMPanelManagerContainer, { panels: initialPanels, direction: "horizontal", showToolbar: true }) }) }), _jsx(TMSearchResult, { isVisible: currentSearchView === TMSearchViews.Result, context: SearchResultContext.METADATA_SEARCH, searchResults: searchResult, onRefreshAfterAddDcmtToFavs: onRefreshAfterAddDcmtToFavs, onRefreshSearchAsync: async () => {
200
+ let newResult = await refreshLastSearch(lastQdSearched) ?? [];
201
+ setSearchResult(newResult);
202
+ if (newResult.length <= 0) {
203
+ setCurrentSearchView(TMSearchViews.Search);
204
+ }
205
+ }, onTaskCreateRequest: onTaskCreateRequest, onClose: () => { setCurrentSearchView(TMSearchViews.Search); }, onFileOpened: onFileOpened, focusedWorkingGroupId: focusedWorkingGroupId, fetchTreeFileSystemForWorkingGroup: fetchTreeFileSystemForWorkingGroup, fetchArchivedDocumentsForWorkingGroup: fetchArchivedDocumentsForWorkingGroup })] }));
202
206
  };
203
207
  export default TMSearch;
204
208
  const TMTreeSelectorWrapper = ({ isMobile, onSelectedTIDChanged }) => {
@@ -349,7 +349,7 @@ const TMSearchResult = ({ context = SearchResultContext.METADATA_SEARCH, isVisib
349
349
  _jsx(TMLayoutItem, { children: _jsx(TMSearchResultSelector, { searchResults: currentSearchResults, selectedTID: selectedSearchResultTID, onSelectionChanged: onSearchResultSelectionChanged }) })
350
350
  :
351
351
  _jsx(_Fragment, {}), _jsxs(TMLayoutItem, { children: [_jsx(TMSearchResultGrid, { inputFocusedItem: focusedItem, inputSelectedItems: selectedItems, searchResult: searchResults.length > 1 ? selectedSearchResult : searchResults[0], lastUpdateSearchTime: lastUpdateSearchTime, onDblClick: () => openFormHandler(LayoutModes.Update), onContextMenuPreparing: onContextMenuPreparing, onSelectionChanged: (items) => { setSelectedItems(items); }, onVisibleItemChanged: setVisibleItems, onFocusedItemChanged: setFocusedItem, onDownloadDcmtsAsync: async (inputDcmts, downloadType, downloadMode, _y, confirmAttachments) => await downloadDcmtsAsync(inputDcmts, downloadType, downloadMode, onFileOpened, confirmAttachments) }), allowFloatingBar && showFloatingBar && deviceType !== DeviceType.MOBILE &&
352
- _jsxs(TMFloatingToolbar, { backgroundColor: TMColors.primaryColor, initialLeft: '10px', initialTop: 'calc(100% - 75px)', children: [fromDTD?.perm?.canRetrieveFile === AccessLevels.Yes && _jsx(TMButton, { btnStyle: 'icon', caption: "Download file", disabled: fromDTD?.perm?.canRetrieveFile !== AccessLevels.Yes || !focusedItem?.DID, icon: _jsx(IconDownload, { color: 'white' }), onClick: () => { downloadDcmtsAsync(getSelectedDcmtsOrFocused(selectedItems, focusedItem), DownloadTypes.Dcmt, "download"); } }), allowRelations && _jsx(TMButton, { btnStyle: 'icon', disabled: !currentTIDHasDetailRelations || !focusedItem?.DID, icon: _jsx(IconDetailDcmts, { color: 'white' }), caption: SDKUI_Localizator.DcmtsDetail, onClick: () => setIsOpenDetails(true) }), allowRelations && _jsx(TMButton, { btnStyle: 'icon', disabled: !currentTIDHasMasterRelations || !focusedItem?.DID, icon: _jsx(IconDetailDcmts, { color: 'white', transform: 'scale(-1, 1)' }), caption: SDKUI_Localizator.DcmtsMaster, onClick: () => setIsOpenMaster(true) }), _jsx(IconMenuVertical, { id: `commands-floating-${id}`, color: 'white', cursor: 'pointer' }), _jsx(CommandsContextMenu, { target: `#commands-floating-${id}`, menuItems: getCommandsMenuItems(fromDTD, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, setShowFloatingBar, openFormHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, showCopyMoveFormCallback) })] })] })] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), isOpenBatchUpdate && _jsx(TMBatchUpdateForm, { isModal: true, titleModal: SDKUI_Localizator.BatchUpdate, inputDcmts: getSelectionDcmtInfo(), TID: focusedItem ? focusedItem?.TID : selectedItems[0]?.TID, DID: focusedItem ? focusedItem?.DID : selectedItems[0]?.DID, onBack: () => {
352
+ _jsxs(TMFloatingToolbar, { backgroundColor: TMColors.primaryColor, initialLeft: '10px', initialTop: 'calc(100% - 75px)', children: [fromDTD?.perm?.canRetrieveFile === AccessLevels.Yes && _jsx(TMButton, { btnStyle: 'icon', caption: "Download file", disabled: fromDTD?.perm?.canRetrieveFile !== AccessLevels.Yes || !focusedItem?.DID, icon: _jsx(IconDownload, { color: 'white' }), onClick: () => { downloadDcmtsAsync(getSelectedDcmtsOrFocused(selectedItems, focusedItem), DownloadTypes.Dcmt, "download"); } }), allowRelations && _jsx(TMButton, { btnStyle: 'icon', disabled: !currentTIDHasDetailRelations || !focusedItem?.DID, icon: _jsx(IconDetailDcmts, { color: 'white' }), caption: SDKUI_Localizator.DcmtsDetail, onClick: () => setIsOpenDetails(true) }), allowRelations && _jsx(TMButton, { btnStyle: 'icon', disabled: !currentTIDHasMasterRelations || !focusedItem?.DID, icon: _jsx(IconDetailDcmts, { color: 'white', transform: 'scale(-1, 1)' }), caption: SDKUI_Localizator.DcmtsMaster, onClick: () => setIsOpenMaster(true) }), _jsx(IconMenuVertical, { id: `commands-floating-${id}`, color: 'white', cursor: 'pointer' }), _jsx(CommandsContextMenu, { target: `#commands-floating-${id}`, menuItems: getCommandsMenuItems(fromDTD, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, setShowFloatingBar, openFormHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, showCopyMoveFormCallback) })] })] }), "m"] }), showApprovePopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 0, onClose: () => setShowApprovePopup(false) }), showRejectPopup && _jsx(WorkFlowApproveRejectPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), isReject: 1, onClose: () => setShowRejectPopup(false) }), showReAssignPopup && _jsx(WorkFlowReAssignPopUp, { deviceType: deviceType, onCompleted: onWFOperationCompleted, selectedItems: getSelectedDcmtsOrFocused(selectedItems, focusedItem), onClose: () => setShowReAssignPopup(false) }), isOpenBatchUpdate && _jsx(TMBatchUpdateForm, { isModal: true, titleModal: SDKUI_Localizator.BatchUpdate, inputDcmts: getSelectionDcmtInfo(), TID: focusedItem ? focusedItem?.TID : selectedItems[0]?.TID, DID: focusedItem ? focusedItem?.DID : selectedItems[0]?.DID, onBack: () => {
353
353
  setIsOpenBatchUpdate(false);
354
354
  }, onSavedCallbackAsync: async () => {
355
355
  setIsOpenBatchUpdate(false);
@@ -30,8 +30,17 @@ export declare class DataGridSettings {
30
30
  export declare class ThemeSettings {
31
31
  fontSize: string;
32
32
  gridSettings: DataGridSettings;
33
- gutters: number;
33
+ /** Calcola dinamicamente i gutters in base alla dimensione attuale della finestra. */
34
+ get gutters(): number;
34
35
  constructor(skipCssUpdate?: boolean);
36
+ /**
37
+ * Controlla la serializzazione JSON di questo oggetto.
38
+ * Escludiamo esplicitamente il getter 'gutters' dall'output, così non verrà mai salvato.
39
+ */
40
+ toJSON(): {
41
+ fontSize: string;
42
+ gridSettings: DataGridSettings;
43
+ };
35
44
  }
36
45
  export declare class SearchSettings {
37
46
  invoiceRetrieveFormat: InvoiceRetrieveFormats;
@@ -63,15 +63,28 @@ export class DataGridSettings {
63
63
  }
64
64
  }
65
65
  export class ThemeSettings {
66
+ /** Calcola dinamicamente i gutters in base alla dimensione attuale della finestra. */
67
+ get gutters() {
68
+ return Gutters.getGutters();
69
+ }
66
70
  constructor(skipCssUpdate = false) {
67
71
  this.fontSize = FontSize.defaultFontSizeInPixel;
68
72
  this.gridSettings = new DataGridSettings();
69
- this.gutters = Gutters.getGutters();
70
73
  // Automatically update the CSS variable for font size
71
74
  if (!skipCssUpdate) {
72
75
  document.documentElement.style.setProperty('--base-font-size', this.fontSize);
73
76
  }
74
77
  }
78
+ /**
79
+ * Controlla la serializzazione JSON di questo oggetto.
80
+ * Escludiamo esplicitamente il getter 'gutters' dall'output, così non verrà mai salvato.
81
+ */
82
+ toJSON() {
83
+ return {
84
+ fontSize: this.fontSize,
85
+ gridSettings: this.gridSettings,
86
+ };
87
+ }
75
88
  }
76
89
  export class SearchSettings {
77
90
  constructor() {
@@ -0,0 +1,11 @@
1
+ import { SearchResultDescriptor } from '@topconsultnpm/sdk-ts-beta';
2
+ /**
3
+ * Hook per gestire i dati di approvazione del workflow.
4
+ * Fornisce il dataSource, lo stato di caricamento e una funzione per aggiornare i dati.
5
+ */
6
+ export declare const useWorkflowApprove: () => {
7
+ workflowApproveData: SearchResultDescriptor[];
8
+ isLoading: boolean;
9
+ refreshWorkflowApprove: () => Promise<void>;
10
+ totalDcmtsFound: number;
11
+ };
@@ -0,0 +1,57 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { SDK_Globals } from '@topconsultnpm/sdk-ts-beta';
3
+ /**
4
+ * Hook per gestire i dati di approvazione del workflow.
5
+ * Fornisce il dataSource, lo stato di caricamento e una funzione per aggiornare i dati.
6
+ */
7
+ export const useWorkflowApprove = () => {
8
+ const [workflowApproveData, setWorkflowApproveData] = useState(SDK_Globals.currentWorkflowApproveData);
9
+ const [isLoading, setIsLoading] = useState(false);
10
+ const [totalDcmtsFound, setTotalDcmtsFound] = useState(0);
11
+ /**
12
+ * Calcola il numero totale di dcmtsFound dai dati del workflow.
13
+ */
14
+ const calculateTotalDcmtsFound = useCallback((data) => {
15
+ return data.reduce((sum, item) => sum + (item.dcmtsFound || 0), 0);
16
+ }, []);
17
+ /**
18
+ * Esegue il fetch dei dati dal workflow, aggiorna lo stato globale
19
+ * e notifica gli altri componenti tramite un evento.
20
+ * Restituisce sempre una Promise<void>.
21
+ */
22
+ const refreshWorkflowApprove = useCallback(async () => {
23
+ // Se la sessione non è valida, esci subito.
24
+ if (!SDK_Globals.tmSession) {
25
+ console.warn("Tentativo di fetch del workflow senza una sessione valida.");
26
+ return;
27
+ }
28
+ setIsLoading(true);
29
+ try {
30
+ const result = await SDK_Globals.tmSession.NewWorkflowEngine().WFApprAsync(1);
31
+ SDK_Globals.currentWorkflowApproveData = result ?? [];
32
+ // Aggiorna anche il totale dei dcmtsFound
33
+ setTotalDcmtsFound(calculateTotalDcmtsFound(SDK_Globals.currentWorkflowApproveData));
34
+ // Notifica tutte le istanze del hook che i dati sono cambiati.
35
+ window.dispatchEvent(new CustomEvent('onWorkflowApproveChange'));
36
+ }
37
+ catch (error) {
38
+ console.error("Errore durante il fetch dei dati di approvazione workflow:", error);
39
+ }
40
+ finally {
41
+ setIsLoading(false);
42
+ }
43
+ }, [calculateTotalDcmtsFound]); // useCallback con array vuoto perché non ha dipendenze esterne a React
44
+ // Questo useEffect serve a sincronizzare lo stato tra diverse istanze del hook.
45
+ // Se il componente A chiama refreshWorkflowApprove, il componente B (che usa lo stesso hook)
46
+ // riceverà l'evento e aggiornerà il suo stato locale.
47
+ useEffect(() => {
48
+ const handleUpdate = () => {
49
+ setWorkflowApproveData(SDK_Globals.currentWorkflowApproveData);
50
+ };
51
+ window.addEventListener('onWorkflowApproveChange', handleUpdate);
52
+ return () => {
53
+ window.removeEventListener('onWorkflowApproveChange', handleUpdate);
54
+ };
55
+ }, [calculateTotalDcmtsFound]);
56
+ return { workflowApproveData, isLoading, refreshWorkflowApprove, totalDcmtsFound };
57
+ };
package/lib/index.d.ts CHANGED
@@ -7,4 +7,5 @@ export * from './hooks/useOutsideClick';
7
7
  export * from './hooks/useQueryParametersDialog';
8
8
  export * from './hooks/useDcmtOperations';
9
9
  export * from './hooks/useResizeObserver';
10
+ export * from './hooks/useWorkflowApprove';
10
11
  export * from './services';
package/lib/index.js CHANGED
@@ -7,6 +7,7 @@ export * from './hooks/useOutsideClick';
7
7
  export * from './hooks/useQueryParametersDialog';
8
8
  export * from './hooks/useDcmtOperations';
9
9
  export * from './hooks/useResizeObserver';
10
+ export * from './hooks/useWorkflowApprove';
10
11
  export * from './services';
11
12
  import config from 'devextreme/core/config';
12
13
  // DevExtreme License Key (valid for v24.2 and earlier versions)
@@ -62,8 +62,9 @@ TMMargin.largeMargin = '10px';
62
62
  class Gutters {
63
63
  static getGutters() {
64
64
  // Detect mobile device (basic check)
65
- const isMobile = /Mobi|Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
66
- return isMobile ? this.defaultMobile : this.defaultDesktop;
65
+ const mobileBreakpoint = 768;
66
+ // Se la larghezza della finestra è inferiore al breakpoint, usiamo i gutters per mobile.
67
+ return window.innerWidth < mobileBreakpoint ? this.defaultMobile : this.defaultDesktop;
67
68
  }
68
69
  }
69
70
  Gutters.defaultDesktop = 20;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react-beta",
3
- "version": "6.14.67",
3
+ "version": "6.14.68",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",