@topconsultnpm/sdkui-react 6.21.0-dev2.3 → 6.21.0-dev2.31

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 (50) hide show
  1. package/lib/components/base/TMAccordionNew.d.ts +1 -0
  2. package/lib/components/base/TMAccordionNew.js +6 -5
  3. package/lib/components/base/TMAreaManager.js +19 -3
  4. package/lib/components/base/TMDataGrid.js +2 -2
  5. package/lib/components/base/TMPanel.d.ts +7 -4
  6. package/lib/components/base/TMPanel.js +58 -26
  7. package/lib/components/choosers/TMDistinctValues.js +35 -21
  8. package/lib/components/editors/TMDateBox.js +4 -2
  9. package/lib/components/editors/TMFormulaEditor.d.ts +2 -0
  10. package/lib/components/editors/TMFormulaEditor.js +75 -21
  11. package/lib/components/editors/TMMetadataValues.js +2 -1
  12. package/lib/components/editors/TMRadioButton.js +2 -1
  13. package/lib/components/editors/TMTextArea.d.ts +2 -0
  14. package/lib/components/editors/TMTextArea.js +6 -3
  15. package/lib/components/features/documents/TMDcmtForm.d.ts +1 -0
  16. package/lib/components/features/documents/TMDcmtForm.js +105 -27
  17. package/lib/components/features/documents/TMDcmtFormActionButtons.js +17 -2
  18. package/lib/components/features/documents/TMDcmtPreview.d.ts +1 -0
  19. package/lib/components/features/documents/TMDcmtPreview.js +2 -2
  20. package/lib/components/features/documents/TMDcmtTasks.d.ts +1 -0
  21. package/lib/components/features/documents/TMDcmtTasks.js +2 -2
  22. package/lib/components/features/search/SignatureParamsManager.d.ts +70 -0
  23. package/lib/components/features/search/SignatureParamsManager.js +145 -0
  24. package/lib/components/features/search/TMSavedQuerySelector.d.ts +2 -2
  25. package/lib/components/features/search/TMSavedQuerySelector.js +3 -2
  26. package/lib/components/features/search/TMSearch.d.ts +2 -1
  27. package/lib/components/features/search/TMSearch.js +15 -9
  28. package/lib/components/features/search/TMSearchQueryPanel.js +1 -1
  29. package/lib/components/features/search/TMSearchResult.js +65 -18
  30. package/lib/components/features/search/TMViewHistoryDcmt.js +1 -2
  31. package/lib/components/features/workflow/diagram/queryDescriptorParser.js +3 -6
  32. package/lib/components/grids/TMBlogAttachments.d.ts +1 -0
  33. package/lib/components/grids/TMBlogAttachments.js +38 -12
  34. package/lib/components/grids/TMBlogsPost.js +7 -1
  35. package/lib/components/grids/TMBlogsPostUtils.js +11 -17
  36. package/lib/components/index.d.ts +1 -0
  37. package/lib/components/index.js +1 -0
  38. package/lib/components/pages/TMPage.js +3 -1
  39. package/lib/components/viewers/TMTidViewer.js +1 -1
  40. package/lib/helper/GlobalStyles.js +6 -0
  41. package/lib/helper/SDKUI_Localizator.d.ts +49 -0
  42. package/lib/helper/SDKUI_Localizator.js +492 -0
  43. package/lib/helper/TMPdfViewer.js +25 -24
  44. package/lib/hooks/useDataUserIdItem.js +6 -4
  45. package/lib/hooks/useDocumentOperations.d.ts +1 -0
  46. package/lib/hooks/useDocumentOperations.js +8 -6
  47. package/lib/hooks/useForm.js +5 -2
  48. package/lib/hooks/useResizeObserver.d.ts +1 -1
  49. package/lib/hooks/useResizeObserver.js +16 -15
  50. package/package.json +2 -2
@@ -57,7 +57,7 @@ export var InvocationContext;
57
57
  let abortControllerLocal = new AbortController();
58
58
  ;
59
59
  //#endregion
60
- const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMode = FormModes.Update, invocationContext = InvocationContext.Default, showHeader = true, showBackButton = true, showDcmtFormSidebar = true, isClosable = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, isModal = false, titleModal, widthModal = "100%", heightModal = "100%", allowNavigation = true, canNext, canPrev, count, itemIndex, onNext, onPrev, inputFile = null, inputMids = [], connectorFileSave = undefined, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowRelations = true, allowButtonsRefs = false, openS4TViewer = false, enableDragDropOverlay = false, onClose, onSavedAsyncCallback, onSaveRecents, onWFOperationCompleted, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, onTaskCompleted, onTaskCreateRequest, moreInfoTasks, taskFormDialogComponent, handleNavigateToWGs, handleNavigateToDossiers, onReferenceClick, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, s4TViewerDialogComponent, onScanRequest, passToSearch, datagridUtility }) => {
60
+ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMode = FormModes.Update, invocationContext = InvocationContext.Default, showHeader = true, showBackButton = true, showDcmtFormSidebar = true, isClosable = false, showTodoDcmtForm = false, isExpertMode = SDKUI_Globals.userSettings.advancedSettings.expertMode === 1, isModal = false, titleModal, widthModal = "100%", heightModal = "100%", allowNavigation = true, canNext, canPrev, count, itemIndex, onNext, onPrev, inputFile = null, inputMids = [], connectorFileSave = undefined, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowRelations = true, allowButtonsRefs = false, openS4TViewer = false, enableDragDropOverlay = false, onClose, onSavedAsyncCallback, onSaveRecents, onWFOperationCompleted, allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, onTaskCompleted, onTaskCreateRequest, moreInfoTasks, taskFormDialogComponent, handleNavigateToWGs, handleNavigateToDossiers, onReferenceClick, onOpenS4TViewerRequest, onOpenPdfEditorRequest, openFileUploaderPdfEditor, s4TViewerDialogComponent, onScanRequest, passToSearch, datagridUtility }) => {
61
61
  const { onRefreshSearchAsyncDatagrid, onRefreshBlogDatagrid, onRefreshPreviewDatagrid } = datagridUtility || {};
62
62
  const floatingBarContainerRef = useRef(null);
63
63
  const [id, setID] = useState('');
@@ -91,6 +91,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
91
91
  const formDataRef = useRef([]);
92
92
  const fromDTDRef = useRef();
93
93
  const dcmtFileRef = useRef(null);
94
+ const metadataDcmtOriginRef = useRef(null);
94
95
  const [isOpenDistinctValues, setIsOpenDistinctValues] = useState(false);
95
96
  const [isOpenFormulaEditor, setIsOpenFormulaEditor] = useState(false);
96
97
  const [currentTIDHasDetailRelations, setCurrentTIDHasDetailRelations] = useState();
@@ -135,7 +136,30 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
135
136
  if (!did)
136
137
  return;
137
138
  TMSpinner.show({ description: 'Loading Metadata...' });
138
- let res = getMetadataResult ?? await SDK_Globals.tmSession?.NewSearchEngine().GetMetadataAsync(TID, did, true);
139
+ let res = getMetadataResult;
140
+ if (!res) {
141
+ try {
142
+ res = await SDK_Globals.tmSession?.NewSearchEngine().GetMetadataAsync(TID, did, true);
143
+ }
144
+ catch (metadataError) {
145
+ // Estrai ErrorCode dal campo detail se disponibile
146
+ let errorCode = undefined;
147
+ if (metadataError?.isApiException && metadataError?.response?.detail) {
148
+ try {
149
+ const detailObj = JSON.parse(metadataError.response.detail);
150
+ errorCode = detailObj?.ErrorCode;
151
+ }
152
+ catch (parseError) {
153
+ console.log("Impossibile parsare il detail dell'eccezione:", parseError);
154
+ }
155
+ }
156
+ // Se ErrorCode è -5, ignora silenziosamente, altrimenti mostra l'eccezione originale
157
+ if (errorCode !== -5) {
158
+ TMExceptionBoxManager.show({ exception: metadataError });
159
+ }
160
+ return;
161
+ }
162
+ }
139
163
  const origin = { fromName: res?.fromName, fromTID: res?.fromTID };
140
164
  let dtd = res?.dtdResult;
141
165
  let rows = dtd.rows ? dtd.rows[0] : [];
@@ -307,7 +331,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
307
331
  }
308
332
  else {
309
333
  const renderedMetadata = dtd?.metadata?.filter((metadata) => handleArchiveVisibility(metadata)) ?? [];
310
- const metadataList = searchResultToMetadataValues(dtd?.id, undefined, [], [], renderedMetadata, layoutMode, isReadOnlyOriginCallback(metadataDcmtOrigin?.fromTID));
334
+ const metadataList = searchResultToMetadataValues(dtd?.id, undefined, [], [], renderedMetadata, layoutMode, isReadOnlyOriginCallback(metadataDcmtOriginRef.current?.fromTID));
311
335
  setFormDataOrig(structuredClone(metadataList));
312
336
  setFormData(structuredClone(metadataList));
313
337
  formDataOrigRef.current = structuredClone(metadataList);
@@ -322,7 +346,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
322
346
  setIsInitialLoading(false);
323
347
  setIsNavigating(false);
324
348
  }
325
- }, [TID, DID, layoutMode, inputFile, setMetadataList, handleReset, allowButtonsRefs, isReadOnlyOriginCallback, metadataDcmtOrigin?.fromTID]);
349
+ }, [TID, DID, layoutMode, inputFile, setMetadataList, handleReset, allowButtonsRefs, isReadOnlyOriginCallback]);
326
350
  const currentSearchResults = useMemo(() => {
327
351
  if (!formData || formData.length === 0 || !TID || !DID)
328
352
  return [];
@@ -477,6 +501,9 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
477
501
  useEffect(() => {
478
502
  dcmtFileRef.current = dcmtFile;
479
503
  }, [dcmtFile]);
504
+ useEffect(() => {
505
+ metadataDcmtOriginRef.current = metadataDcmtOrigin;
506
+ }, [metadataDcmtOrigin]);
480
507
  useEffect(() => {
481
508
  if (!inputFile || inputFile === null)
482
509
  return;
@@ -775,7 +802,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
775
802
  // Verifica che ci siano riferimenti renderizzabili (solo Dossier o WorkingGroup)
776
803
  return dcmtReferences.some(ref => ref.objClass === ObjectClasses.Dossier || ref.objClass === ObjectClasses.WorkingGroup);
777
804
  }, [allowButtonsRefs, layoutMode, dcmtReferences, isOpenDetails, isOpenMaster]);
778
- const isToppyVisible = useMemo(() => Boolean((showToppyForApprove || showToppyForCompleteMoreInfo || showToppyForReferences) && !openS4TViewer), [showToppyForApprove, showToppyForCompleteMoreInfo, showToppyForReferences, openS4TViewer]);
805
+ const isToppyVisible = useMemo(() => Boolean((showToppyForApprove || showToppyForCompleteMoreInfo || showToppyForReferences) && !openS4TViewer && !showTodoDcmtForm), [showToppyForApprove, showToppyForCompleteMoreInfo, showToppyForReferences, openS4TViewer, showTodoDcmtForm]);
779
806
  const isModified = useMemo(() => calcIsModified(formData, formDataOrig), [formData, formDataOrig]);
780
807
  const formToolbar = useMemo(() => _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '10px' }, children: [allowNavigation && canPrev != undefined && _jsx("p", { style: { textAlign: 'center', padding: '1px 4px', display: 'flex' }, children: `${itemIndex}/${count}` }), allowNavigation && canPrev != undefined && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', iconColor: 'white', isModified: isModified, formMode: formMode, canPrev: canPrev, onPrev: onPrev }), allowNavigation && canNext != undefined && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', iconColor: 'white', isModified: isModified, formMode: formMode, canNext: canNext, onNext: onNext }), layoutMode === LayoutModes.Update &&
781
808
  _jsx(ContextMenu, { items: operationItems, trigger: "left", children: _jsx(IconMenuVertical, { color: 'white', cursor: 'pointer' }) }), layoutMode === LayoutModes.Ark &&
@@ -1016,6 +1043,21 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1016
1043
  fd.tid = TID;
1017
1044
  return fd;
1018
1045
  }, [focusedMetadataValue?.value, focusedMetadataValue?.mid, TID]);
1046
+ /**
1047
+ * Restituisce l'handler onBack per i pannelli, evitando duplicazione di codice.
1048
+ * @param isMainPanel - Se true, è il pannello principale (tmDcmtForm)
1049
+ */
1050
+ const getOnBackHandler = (isMainPanel = false) => {
1051
+ // Se showBackButton è false, non mostrare mai il back
1052
+ if (showBackButton === false)
1053
+ return undefined;
1054
+ // Mobile: mostra back su tutti i pannelli (se showBackButton è definito e non è closable)
1055
+ if (isMobile) {
1056
+ return (showBackButton !== undefined && !isClosable) ? handleClose : undefined;
1057
+ }
1058
+ // Desktop: mostra back solo sul pannello principale (se showBackButton è definito e non è closable)
1059
+ return (showBackButton !== undefined && isMainPanel && !isClosable) ? handleClose : undefined;
1060
+ };
1019
1061
  // Determina se showAll deve essere automaticamente true
1020
1062
  // Best practice: usa useMemo per calcolare valori derivati invece di useEffect con setState
1021
1063
  const shouldShowAll = useMemo(() => {
@@ -1100,7 +1142,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1100
1142
  ]);
1101
1143
  const tmBlog = useMemo(() => _jsx(TMDcmtBlog, { tid: TID, did: DID, fetchBlogDataTrigger: refreshBlogTrigger, onRefreshBlogDatagrid: onRefreshBlogDatagrid, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }), [TID, DID, allTasks, refreshBlogTrigger, handleNavigateToWGs, handleNavigateToDossiers]);
1102
1144
  const tmSysMetadata = useMemo(() => _jsx(TMMetadataValues, { layoutMode: layoutMode, openChooserBySingleClick: !isOpenDistinctValues, TID: TID, isReadOnly: true, deviceType: deviceType, metadataValues: formData.filter(o => (o.mid != undefined && o.mid <= 100)), metadataValuesOrig: formData.filter(o => (o.mid != undefined && o.mid <= 100)), validationItems: [], inputMids: inputMids }), [TID, layoutMode, formData, deviceType, inputMids]);
1103
- const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { refreshPreviewTrigger: refreshPreviewTrigger, fromDTD: fromDTD, currentDcmt: currentDcmt, dcmtFile: dcmtFile ?? inputFile, deviceType: deviceType, layoutMode: layoutMode, onFileUpload: (file) => { setDcmtFile(file); }, openFileUploaderPdfEditor: openFileUploaderPdfEditor, enableDragDropOverlay: enableDragDropOverlay, onScanRequest: onScanRequest }), [currentDcmt, dcmtFile, deviceType, fromDTD, layoutMode, inputFile, enableDragDropOverlay, setDcmtFile, onScanRequest, openFileUploaderPdfEditor, refreshPreviewTrigger]);
1145
+ const tmDcmtPreview = useMemo(() => _jsx(TMDcmtPreviewWrapper, { refreshPreviewTrigger: refreshPreviewTrigger, fromDTD: fromDTD, currentDcmt: currentDcmt, dcmtFile: dcmtFile ?? inputFile, deviceType: deviceType, onBack: getOnBackHandler(), layoutMode: layoutMode, onFileUpload: (file) => { setDcmtFile(file); }, openFileUploaderPdfEditor: openFileUploaderPdfEditor, enableDragDropOverlay: enableDragDropOverlay, onScanRequest: onScanRequest }), [currentDcmt, dcmtFile, deviceType, fromDTD, layoutMode, inputFile, enableDragDropOverlay, setDcmtFile, onScanRequest, openFileUploaderPdfEditor, refreshPreviewTrigger]);
1104
1146
  const tmWF = useMemo(() => {
1105
1147
  if (isWFDataLoading) {
1106
1148
  return (_jsx("div", { style: {
@@ -1171,7 +1213,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1171
1213
  did: Number(DID),
1172
1214
  name: fromDTD?.nameLoc ?? SDKUI_Localizator.Widget_Activities,
1173
1215
  },
1174
- }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, afterTaskSaved: afterTaskSaved, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers }));
1216
+ }, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, afterTaskSaved: afterTaskSaved, handleNavigateToWGs: handleNavigateToWGs, handleNavigateToDossiers: handleNavigateToDossiers, onBack: getOnBackHandler() }));
1175
1217
  }, [allTasks, TID, DID, fromDTD]);
1176
1218
  const normalizedTID = TID !== undefined ? Number(TID) : undefined;
1177
1219
  const defaultPanelDimensions = {
@@ -1230,7 +1272,7 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1230
1272
  showHeader: showHeader,
1231
1273
  title: titleDcmtFormPanel,
1232
1274
  allowMaximize: !isMobile,
1233
- onBack: showBackButton ? (isClosable && deviceType !== DeviceType.MOBILE) ? undefined : handleClose : undefined,
1275
+ onBack: getOnBackHandler(true),
1234
1276
  onClose: isClosable ? () => { } : undefined,
1235
1277
  toolbar: allowNavigation ? formToolbar : _jsx(_Fragment, {})
1236
1278
  },
@@ -1245,7 +1287,14 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1245
1287
  {
1246
1288
  id: 'tmBlog',
1247
1289
  name: SDKUI_Localizator.BlogCase,
1248
- contentOptions: { component: tmBlog, panelContainer: { title: SDKUI_Localizator.BlogCase, allowMaximize: !isMobile } },
1290
+ contentOptions: {
1291
+ component: tmBlog,
1292
+ panelContainer: {
1293
+ title: SDKUI_Localizator.BlogCase,
1294
+ allowMaximize: !isMobile,
1295
+ onBack: getOnBackHandler(),
1296
+ }
1297
+ },
1249
1298
  toolbarOptions: {
1250
1299
  icon: _jsx(IconBoard, { fontSize: 24 }),
1251
1300
  visible: getDcmtFormToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmBlog,
@@ -1257,7 +1306,14 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1257
1306
  {
1258
1307
  id: 'tmSysMetadata',
1259
1308
  name: SDKUI_Localizator.MetadataSystem,
1260
- contentOptions: { component: tmSysMetadata, panelContainer: { title: SDKUI_Localizator.MetadataSystem, allowMaximize: !isMobile } },
1309
+ contentOptions: {
1310
+ component: tmSysMetadata,
1311
+ panelContainer: {
1312
+ title: SDKUI_Localizator.MetadataSystem,
1313
+ allowMaximize: !isMobile,
1314
+ onBack: getOnBackHandler(),
1315
+ }
1316
+ },
1261
1317
  toolbarOptions: { icon: _jsx(IconDcmtTypeSys, { fontSize: 24 }), visible: getDcmtFormToolbarVisibility(SDK_Globals.tmSession?.SessionDescr?.appModuleID ?? AppModules.SURFER).tmSysMetadata, disabled: isSysMetadataDisabled, orderNumber: 3, isActive: allInitialPanelVisibility['tmSysMetadata'] }
1262
1318
  },
1263
1319
  {
@@ -1278,7 +1334,8 @@ const TMDcmtForm = ({ TID, DID, groupId, layoutMode = LayoutModes.Update, formMo
1278
1334
  contentOptions: {
1279
1335
  component: tmWF,
1280
1336
  panelContainer: {
1281
- title: "Workflow", allowMaximize: !isMobile
1337
+ title: "Workflow", allowMaximize: !isMobile,
1338
+ onBack: getOnBackHandler(),
1282
1339
  }
1283
1340
  },
1284
1341
  toolbarOptions: {
@@ -1537,38 +1594,59 @@ const validateMaxLength = (mvd, value, validationItems) => {
1537
1594
  //#endregion Validation
1538
1595
  // Synchronizes panel visibility and toolbar button disabled states when panels become disabled
1539
1596
  const PanelDisabledStateHandler = ({ isWFDisabled, isSysMetadataDisabled, isBoardDisabled, isDcmtTasksDisabled, isPreviewDisabled }) => {
1540
- const { setPanelVisibilityById, setToolbarButtonDisabled, panelVisibility } = useTMPanelManagerContext();
1597
+ const { setPanelVisibilityById, setToolbarButtonDisabled } = useTMPanelManagerContext();
1541
1598
  useEffect(() => {
1542
- // Aggiorna lo stato disabled del bottone toolbar
1543
- setToolbarButtonDisabled('tmSysMetadata', isSysMetadataDisabled);
1544
- setToolbarButtonDisabled('tmBlog', isBoardDisabled);
1545
- setToolbarButtonDisabled('tmWF', isWFDisabled);
1546
- setToolbarButtonDisabled('tmDcmtTasks', isDcmtTasksDisabled);
1547
- setToolbarButtonDisabled('tmDcmtPreview', isPreviewDisabled);
1548
- // Chiude il pannello solo se è attualmente visibile e deve essere disabilitato
1549
- if (isSysMetadataDisabled && panelVisibility['tmSysMetadata']) {
1599
+ if (isSysMetadataDisabled) {
1600
+ setToolbarButtonDisabled('tmSysMetadata', true);
1550
1601
  setPanelVisibilityById('tmSysMetadata', false);
1551
1602
  }
1552
- if (isBoardDisabled && panelVisibility['tmBlog']) {
1603
+ else {
1604
+ setToolbarButtonDisabled('tmSysMetadata', false);
1605
+ }
1606
+ }, [isSysMetadataDisabled]);
1607
+ useEffect(() => {
1608
+ if (isBoardDisabled) {
1609
+ setToolbarButtonDisabled('tmBlog', true);
1553
1610
  setPanelVisibilityById('tmBlog', false);
1554
1611
  }
1555
- if (isWFDisabled && panelVisibility['tmWF']) {
1612
+ else {
1613
+ setToolbarButtonDisabled('tmBlog', false);
1614
+ }
1615
+ }, [isBoardDisabled]);
1616
+ useEffect(() => {
1617
+ if (isWFDisabled) {
1618
+ setToolbarButtonDisabled('tmWF', true);
1556
1619
  setPanelVisibilityById('tmWF', false);
1557
1620
  }
1558
- if (isDcmtTasksDisabled && panelVisibility['tmDcmtTasks']) {
1621
+ else {
1622
+ setToolbarButtonDisabled('tmWF', false);
1623
+ }
1624
+ }, [isWFDisabled]);
1625
+ useEffect(() => {
1626
+ if (isDcmtTasksDisabled) {
1627
+ setToolbarButtonDisabled('tmDcmtTasks', true);
1559
1628
  setPanelVisibilityById('tmDcmtTasks', false);
1560
1629
  }
1561
- if (isPreviewDisabled && panelVisibility['tmDcmtPreview']) {
1630
+ else {
1631
+ setToolbarButtonDisabled('tmDcmtTasks', false);
1632
+ }
1633
+ }, [isDcmtTasksDisabled]);
1634
+ useEffect(() => {
1635
+ if (isPreviewDisabled) {
1636
+ setToolbarButtonDisabled('tmDcmtPreview', true);
1562
1637
  setPanelVisibilityById('tmDcmtPreview', false);
1563
1638
  }
1564
- }, [isSysMetadataDisabled, isBoardDisabled, isWFDisabled, isDcmtTasksDisabled, isPreviewDisabled, setPanelVisibilityById, setToolbarButtonDisabled, panelVisibility]);
1639
+ else {
1640
+ setToolbarButtonDisabled('tmDcmtPreview', false);
1641
+ }
1642
+ }, [isPreviewDisabled]);
1565
1643
  return null;
1566
1644
  };
1567
- const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, fromDTD, currentDcmt, layoutMode, dcmtFile, deviceType, isVisible, onFileUpload, openFileUploaderPdfEditor, enableDragDropOverlay = false, onScanRequest }) => {
1645
+ const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, fromDTD, currentDcmt, layoutMode, dcmtFile, deviceType, isVisible, onFileUpload, openFileUploaderPdfEditor, enableDragDropOverlay = false, onScanRequest, onBack }) => {
1568
1646
  const { setPanelVisibilityById, toggleMaximize, isResizingActive, countVisibleLeafPanels, panelVisibility } = useTMPanelManagerContext();
1569
1647
  const isMobile = deviceType === DeviceType.MOBILE;
1570
1648
  return (layoutMode === LayoutModes.Update ?
1571
- _jsx(TMDcmtPreview, { dcmtData: currentDcmt, isVisible: isVisible, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, isResizingActive: isResizingActive }, refreshPreviewTrigger) :
1649
+ _jsx(TMDcmtPreview, { dcmtData: currentDcmt, isVisible: isVisible, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, onBack: onBack, isResizingActive: isResizingActive }, refreshPreviewTrigger) :
1572
1650
  _jsx(TMFileUploader, { fromDTD: fromDTD, onFileUpload: onFileUpload, openFileUploaderPdfEditor: openFileUploaderPdfEditor, onClose: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, isRequired: fromDTD?.archiveConstraint === ArchiveConstraints.ContentCompulsory && dcmtFile === null, defaultBlob: dcmtFile, deviceType: deviceType, isResizingActive: isResizingActive, enableDragDropOverlay: panelVisibility['tmDcmtPreview'] && enableDragDropOverlay, onScanRequest: onScanRequest }));
1573
1651
  };
1574
1652
  const Ribbon = styled.div `
@@ -19,6 +19,18 @@ const TMDcmtFormActionButtons = (props) => {
19
19
  return null;
20
20
  return moreInfoTasks[0];
21
21
  }, [moreInfoTasks]);
22
+ // Verifica se la More Info Card sarà effettivamente renderizzata (non null)
23
+ const isMoreInfoCardVisible = useMemo(() => {
24
+ if (!firstTask)
25
+ return false;
26
+ const userID = SDK_Globals.tmSession?.SessionDescr?.userID;
27
+ const isSender = firstTask?.fromID !== undefined && firstTask.fromID === userID;
28
+ const isRecipient = firstTask?.toID !== undefined && firstTask.toID === userID;
29
+ // Caso 4: L'utente è sia mittente che destinatario - la card non viene renderizzata
30
+ if (isSender && isRecipient)
31
+ return false;
32
+ return true;
33
+ }, [firstTask]);
22
34
  const { hasMoreInfo, hasApprove, hasReferences } = useMemo(() => {
23
35
  const referencesExist = showToppyForReferences && dcmtReferences?.some(ref => ref.objClass === ObjectClasses.Dossier || ref.objClass === ObjectClasses.WorkingGroup);
24
36
  return {
@@ -56,6 +68,9 @@ const TMDcmtFormActionButtons = (props) => {
56
68
  const senderNameTruncated = task?.fromName ? truncate(task.fromName, 30) : 'N/A';
57
69
  const recipientNameTruncated = task?.toName ? truncate(task.toName, 30) : 'N/A';
58
70
  const taskNameTrunc = task?.name ? truncate(task.name.replace(TASK_MORE_INFO_PREFIX_NAME ?? '', ''), 30) : 'N/A';
71
+ // Caso 4: L'utente è sia mittente che destinatario - non renderizzare nulla
72
+ if (isSender && isRecipient)
73
+ return null;
59
74
  return (_jsxs("div", { style: { position: 'relative', display: 'flex' }, children: [_jsxs("div", { style: {
60
75
  padding: '10px',
61
76
  color: '#FFFFFF',
@@ -156,10 +171,10 @@ const TMDcmtFormActionButtons = (props) => {
156
171
  e.currentTarget.style.transform = 'translateY(-50%)';
157
172
  }, children: formatBadgeCount(othersCount) }))] }));
158
173
  };
159
- return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: [hasMoreInfo && firstTask && (_jsx("div", { style: { display: 'flex', gap: "10px", flexDirection: 'column', alignItems: 'center', paddingRight: tasksNumber > 1 ? '36px' : '0' }, children: renderMoreInfoCard(firstTask, tasksNumber > 1) })), hasMoreInfo && hasApprove && _jsx(Divider, {}), hasApprove && (workItems.length === 1 ?
174
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: [hasMoreInfo && firstTask && isMoreInfoCardVisible && (_jsx("div", { style: { display: 'flex', gap: "10px", flexDirection: 'column', alignItems: 'center', paddingRight: tasksNumber > 1 ? '36px' : '0' }, children: renderMoreInfoCard(firstTask, tasksNumber > 1) })), hasMoreInfo && isMoreInfoCardVisible && hasApprove && _jsx(Divider, {}), hasApprove && (workItems.length === 1 ?
160
175
  _jsx(WorkFlowOperationButtons, { dtd: fromDTD, deviceType: deviceType, onApprove: () => updateShowApprovePopup(true), onSignApprove: handleSignApprove, onReject: () => updateShowRejectPopup(true), onReAssign: () => updateShowReAssignPopup(true), onMoreInfo: () => updateShowMoreInfoPopup(true) })
161
176
  :
162
- _jsxs("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: [`Questo documento è associato a ${workItems.length} workitem.`, _jsx("br", {}), `Per approvare, vai alla pagina "Approvazione workflow".`] })), (hasApprove && hasReferences) || (hasMoreInfo && !hasApprove && hasReferences) ? (_jsx(Divider, {})) : null, hasReferences && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px', alignItems: 'center' }, children: [dossierRefs.length > 0 && (_jsx("div", { style: { paddingRight: dossierRefs.length > 1 ? '36px' : '0' }, children: renderReferenceCard(dossierRefs[0], dossierRefs, dossierRefs.length > 1, () => setShowAllDossiersModal(true)) })), workingGroupRefs.length > 0 && (_jsx("div", { style: { paddingRight: workingGroupRefs.length > 1 ? '36px' : '0' }, children: renderReferenceCard(workingGroupRefs[0], workingGroupRefs, workingGroupRefs.length > 1, () => setShowAllWorkingGroupsModal(true)) }))] }))] }), showAllMoreInfoModal && moreInfoTasks && moreInfoTasks.length > 1 && (_jsx(TMModal, { title: `Altre richieste maggiori informazioni (${moreInfoTasks.length - 1})`, onClose: () => setShowAllMoreInfoModal(false), width: calcResponsiveSizes(deviceType, '450px', '450px', '95%'), height: 'auto', children: _jsx("div", { style: {
177
+ _jsxs("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: [`Questo documento è associato a ${workItems.length} workitem.`, _jsx("br", {}), `Per approvare, vai alla pagina "Approvazione workflow".`] })), (hasApprove && hasReferences) || (hasMoreInfo && isMoreInfoCardVisible && !hasApprove && hasReferences) ? (_jsx(Divider, {})) : null, hasReferences && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px', alignItems: 'center' }, children: [dossierRefs.length > 0 && (_jsx("div", { style: { paddingRight: dossierRefs.length > 1 ? '36px' : '0' }, children: renderReferenceCard(dossierRefs[0], dossierRefs, dossierRefs.length > 1, () => setShowAllDossiersModal(true)) })), workingGroupRefs.length > 0 && (_jsx("div", { style: { paddingRight: workingGroupRefs.length > 1 ? '36px' : '0' }, children: renderReferenceCard(workingGroupRefs[0], workingGroupRefs, workingGroupRefs.length > 1, () => setShowAllWorkingGroupsModal(true)) }))] }))] }), showAllMoreInfoModal && moreInfoTasks && moreInfoTasks.length > 1 && (_jsx(TMModal, { title: `Altre richieste maggiori informazioni (${moreInfoTasks.length - 1})`, onClose: () => setShowAllMoreInfoModal(false), width: calcResponsiveSizes(deviceType, '450px', '450px', '95%'), height: 'auto', children: _jsx("div", { style: {
163
178
  display: 'flex',
164
179
  flexDirection: 'column',
165
180
  gap: '8px',
@@ -9,6 +9,7 @@ interface ITMDcmtPreviewProps {
9
9
  onNext?: () => void;
10
10
  onPrev?: () => void;
11
11
  onClosePanel?: () => void;
12
+ onBack?: () => void;
12
13
  allowMaximize?: boolean;
13
14
  onMaximizePanel?: () => void;
14
15
  }
@@ -22,7 +22,7 @@ const ErrorContent = ({ error, isAbortError, onRetry }) => {
22
22
  }
23
23
  return _jsx(TMNothingToShow, { icon: _jsx(IconCloseOutline, { fontSize: 92, color: TMColors.error }), text: error });
24
24
  };
25
- const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev, onClosePanel, onNext, onPrev, allowMaximize = true, onMaximizePanel }) => {
25
+ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev, onClosePanel, onBack, onNext, onPrev, allowMaximize = true, onMaximizePanel }) => {
26
26
  const [dcmtBlob, setDcmtBlob] = useState(undefined);
27
27
  const [showPreview, setShowPreview] = useState(false);
28
28
  const [isFromCache, setIsFromCache] = useState(false);
@@ -183,7 +183,7 @@ const TMDcmtPreview = ({ dcmtData, isResizingActive, isVisible, canNext, canPrev
183
183
  { icon: _jsx(IconCloseCircle, {}), name: SDKUI_Localizator.RemoveFromCache, onClick: () => { removeDcmtsFileCache(cacheKey); setIsFromCache(false); } },
184
184
  { icon: _jsx(IconClear, {}), name: SDKUI_Localizator.ClearCache, onClick: () => { clearDcmtsFileCache(); setIsFromCache(false); } },
185
185
  ], [cacheKey, removeDcmtsFileCache, clearDcmtsFileCache, setIsFromCache]);
186
- return (_jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: _jsx(TMPanel, { padding: '0', title: titleHandler(), onClose: onClosePanel, allowMaximize: allowMaximize, onMaximize: onMaximizePanel, onHeaderDoubleClick: onMaximizePanel, toolbar: _jsxs("div", { style: { width: 'max-content', display: 'flex', alignItems: 'center', gap: '10px' }, children: [onPrev && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', isModified: false, formMode: FormModes.ReadOnly, canPrev: canPrev, onPrev: onPrev }), onNext && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', isModified: false, formMode: FormModes.ReadOnly, canNext: canNext, onNext: onNext }), _jsx(StyledHeaderIcon, { "$color": TMColors.primaryColor, children: _jsx(ContextMenu, { items: cacheMenuItems, trigger: "left", children: _jsx(IconMenuVertical, {}) }) }), _jsx(StyledHeaderIcon, { onClick: reOpenDcmt, "$color": TMColors.primaryColor, children: _jsx(TMTooltip, { content: SDKUI_Localizator.ReopenDocument, children: _jsx(IconRefresh, {}) }) })] }), children: error
186
+ return (_jsx(TMLayoutWaitingContainer, { direction: 'vertical', showWaitPanel: showWaitPanel, showWaitPanelPrimary: showPrimary, showWaitPanelSecondary: showSecondary, waitPanelTitle: waitPanelTitle, waitPanelTextPrimary: waitPanelTextPrimary, waitPanelValuePrimary: waitPanelValuePrimary, waitPanelMaxValuePrimary: waitPanelMaxValuePrimary, waitPanelTextSecondary: waitPanelTextSecondary, waitPanelValueSecondary: waitPanelValueSecondary, waitPanelMaxValueSecondary: waitPanelMaxValueSecondary, isCancelable: true, abortController: abortController, children: _jsx(TMPanel, { padding: '0', title: titleHandler(), onClose: onClosePanel, onBack: onBack, allowMaximize: allowMaximize, onMaximize: onMaximizePanel, onHeaderDoubleClick: onMaximizePanel, toolbar: _jsxs("div", { style: { width: 'max-content', display: 'flex', alignItems: 'center', gap: '10px' }, children: [onPrev && _jsx(TMSaveFormButtonPrevious, { btnStyle: 'icon', isModified: false, formMode: FormModes.ReadOnly, canPrev: canPrev, onPrev: onPrev }), onNext && _jsx(TMSaveFormButtonNext, { btnStyle: 'icon', isModified: false, formMode: FormModes.ReadOnly, canNext: canNext, onNext: onNext }), _jsx(StyledHeaderIcon, { "$color": TMColors.primaryColor, children: _jsx(ContextMenu, { items: cacheMenuItems, trigger: "left", children: _jsx(IconMenuVertical, {}) }) }), _jsx(StyledHeaderIcon, { onClick: reOpenDcmt, "$color": TMColors.primaryColor, children: _jsx(TMTooltip, { content: SDKUI_Localizator.ReopenDocument, children: _jsx(IconRefresh, {}) }) })] }), children: error
187
187
  ? _jsx(ErrorContent, { error: error, isAbortError: isAbortError, onRetry: reOpenDcmt })
188
188
  : renderedPreview(dcmtData?.tid, dcmtData?.did, dcmtData?.fileExt, dcmtData?.fileSize, dcmtData?.fileCount, extensionHandler(dcmtData?.fileExt), showPreview, isResizingActive, () => { isBasketMode ? loadBasketFile() : loadDocumentWithCache(); setShowPreview(true); }, dcmtBlob, isBasketMode) }) }));
189
189
  };
@@ -10,6 +10,7 @@ interface TMDcmtTasksProps {
10
10
  afterTaskSaved: (task: TaskDescriptor | undefined, formMode: FormModes | undefined, forceRefresh?: boolean) => Promise<void>;
11
11
  handleNavigateToWGs?: (value: HomeBlogPost | number) => Promise<void>;
12
12
  handleNavigateToDossiers?: (value: HomeBlogPost | number) => Promise<void>;
13
+ onBack?: () => void;
13
14
  }
14
15
  declare const TMDcmtTasks: (props: TMDcmtTasksProps) => import("react/jsx-runtime").JSX.Element;
15
16
  export default TMDcmtTasks;
@@ -6,7 +6,7 @@ import { useTMPanelManagerContext } from "../../layout/panelManager/TMPanelManag
6
6
  import TMPanel from "../../base/TMPanel";
7
7
  import TMTasksPanelContent from "../tasks/TMTasksPanelContent";
8
8
  const TMDcmtTasks = (props) => {
9
- const { taskContext, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, afterTaskSaved, handleNavigateToWGs, handleNavigateToDossiers } = props;
9
+ const { taskContext, allTasks, getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, afterTaskSaved, handleNavigateToWGs, handleNavigateToDossiers, onBack } = props;
10
10
  // Get the current device type (e.g., mobile, tablet, desktop) using a custom hook.
11
11
  const deviceType = useDeviceType();
12
12
  // This avoids unnecessary re-renders by only recalculating when deviceType changes.
@@ -19,6 +19,6 @@ const TMDcmtTasks = (props) => {
19
19
  text: SDKUI_Localizator.Refresh,
20
20
  },
21
21
  ], children: _jsx(IconMenuVertical, { id: "TMTaksPanel-Commands-Header", color: 'white', cursor: 'pointer' }) }), []);
22
- return _jsx("div", { style: { width: "100%", height: "100%", position: 'relative' }, children: _jsx(TMPanel, { title: SDKUI_Localizator.Widget_Activities, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onClose: countVisibleLeafPanels() > 1 ? () => togglePanelVisibility("tmDcmtTasks") : undefined, onMaximize: countVisibleLeafPanels() > 1 ? () => toggleMaximize("tmDcmtTasks") : undefined, toolbar: toolbar, children: _jsx(TMTasksPanelContent, { id: "dcmtTasks", taskContext: taskContext, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs ? handleNavigateToWGs : () => { return Promise.resolve(); }, handleNavigateToDossiers: handleNavigateToDossiers ? handleNavigateToDossiers : () => { return Promise.resolve(); }, afterTaskSaved: afterTaskSaved }) }) });
22
+ return _jsx("div", { style: { width: "100%", height: "100%", position: 'relative' }, children: _jsx(TMPanel, { title: SDKUI_Localizator.Widget_Activities, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onClose: countVisibleLeafPanels() > 1 ? () => togglePanelVisibility("tmDcmtTasks") : undefined, onMaximize: countVisibleLeafPanels() > 1 ? () => toggleMaximize("tmDcmtTasks") : undefined, toolbar: toolbar, onBack: onBack, children: _jsx(TMTasksPanelContent, { id: "dcmtTasks", taskContext: taskContext, allTasks: allTasks, getAllTasks: getAllTasks, deleteTaskByIdsCallback: deleteTaskByIdsCallback, addTaskCallback: addTaskCallback, editTaskCallback: editTaskCallback, handleNavigateToWGs: handleNavigateToWGs ? handleNavigateToWGs : () => { return Promise.resolve(); }, handleNavigateToDossiers: handleNavigateToDossiers ? handleNavigateToDossiers : () => { return Promise.resolve(); }, afterTaskSaved: afterTaskSaved }) }) });
23
23
  };
24
24
  export default TMDcmtTasks;
@@ -0,0 +1,70 @@
1
+ export declare const DEFAULT_SIGNATURE_TID = -1;
2
+ export type SignatureMethodCode = 'T' | 'D' | 'U';
3
+ export interface SignatureEntry {
4
+ /** TID del tipo documento associato alla firma (-1 = firma di default globale) */
5
+ tid: number;
6
+ /** Immagine della firma in formato base64 (data URL) */
7
+ imgSign: string;
8
+ /** Metodo utilizzato: 'T' = Testo, 'D' = Disegno, 'U' = Upload */
9
+ method: SignatureMethodCode;
10
+ /** Testo utilizzato per generare la firma (solo per method='T') */
11
+ signText?: string;
12
+ }
13
+ export interface SignatureResult {
14
+ /** Immagine della firma in formato base64 (data URL) */
15
+ imgSign: string;
16
+ /** true se è stata usata la firma di default (tid = -1) */
17
+ isDefault: boolean;
18
+ /** TID effettivo della firma trovata */
19
+ sourceTid: number;
20
+ /** Metodo utilizzato: 'T' = Testo, 'D' = Disegno, 'U' = Upload */
21
+ method: SignatureMethodCode;
22
+ /** Testo utilizzato per generare la firma (solo per method='T') */
23
+ signText?: string;
24
+ }
25
+ /**
26
+ * Classe helper per gestire le firme predefinite salvate come JSON array.
27
+ * Centralizza tutte le operazioni di lettura/scrittura/eliminazione.
28
+ */
29
+ export declare class SignatureParamsManager {
30
+ private static cache;
31
+ /**
32
+ * Svuota la cache locale. Utile per forzare un refresh dei dati.
33
+ */
34
+ static clearCache(): void;
35
+ /**
36
+ * Recupera tutte le firme salvate (usa cache locale se disponibile)
37
+ */
38
+ static getAll(): Promise<SignatureEntry[]>;
39
+ /**
40
+ * Recupera la firma per un TID specifico (senza fallback).
41
+ * Restituisce solo la firma associata al TID richiesto, o undefined se non esiste.
42
+ */
43
+ static getByTid(tid: number): Promise<SignatureResult | undefined>;
44
+ /**
45
+ * Recupera la firma per un TID specifico con fallback alla firma di default.
46
+ * Se non trova la firma per il TID specifico, prova con la firma di default (tid = -1).
47
+ * Se non esiste neanche quella, restituisce undefined.
48
+ * Restituisce anche l'informazione se è stata usata la firma di default.
49
+ */
50
+ static getByTidWithFallback(tid: number): Promise<SignatureResult | undefined>;
51
+ /**
52
+ * Salva la firma di default valida per tutti i tipi documento (tid = 0)
53
+ * @param imgSign Immagine firma in base64
54
+ * @param method Metodo utilizzato per creare la firma
55
+ * @param signText Testo utilizzato per generare la firma (solo per method='T')
56
+ */
57
+ static saveDefault(imgSign: string, method: SignatureMethodCode, signText?: string): Promise<void>;
58
+ /**
59
+ * Salva o aggiorna la firma per un TID specifico
60
+ * @param tid ID del tipo documento
61
+ * @param imgSign Immagine firma in base64
62
+ * @param method Metodo utilizzato per creare la firma
63
+ * @param signText Testo utilizzato per generare la firma (solo per method='T')
64
+ */
65
+ static save(tid: number, imgSign: string, method: SignatureMethodCode, signText?: string): Promise<void>;
66
+ /**
67
+ * Elimina la firma per un TID specifico
68
+ */
69
+ static delete(tid: number): Promise<void>;
70
+ }
@@ -0,0 +1,145 @@
1
+ import { SDK_Globals } from "@topconsultnpm/sdk-ts";
2
+ const SIGNATURE_PARAM_KEY = 'signParams'; // Chiave unica parametro per tutte le firme predefinite (JSON array)
3
+ export const DEFAULT_SIGNATURE_TID = -1; // TID speciale per firma di default valida per tutti i tipi documento
4
+ /**
5
+ * Classe helper per gestire le firme predefinite salvate come JSON array.
6
+ * Centralizza tutte le operazioni di lettura/scrittura/eliminazione.
7
+ */
8
+ export class SignatureParamsManager {
9
+ /**
10
+ * Svuota la cache locale. Utile per forzare un refresh dei dati.
11
+ */
12
+ static clearCache() {
13
+ this.cache = null;
14
+ }
15
+ /**
16
+ * Recupera tutte le firme salvate (usa cache locale se disponibile)
17
+ */
18
+ static async getAll() {
19
+ // Se la cache è valorizzata, restituisci direttamente
20
+ if (this.cache !== null) {
21
+ return this.cache;
22
+ }
23
+ const jsonValue = await SDK_Globals.tmSession?.NewUserEngine().ParamGetAsync(SIGNATURE_PARAM_KEY);
24
+ if (!jsonValue || jsonValue === '') {
25
+ this.cache = [];
26
+ return this.cache;
27
+ }
28
+ try {
29
+ this.cache = JSON.parse(jsonValue);
30
+ return this.cache;
31
+ }
32
+ catch {
33
+ console.warn('Errore parsing JSON firme, formato non valido');
34
+ this.cache = [];
35
+ return this.cache;
36
+ }
37
+ }
38
+ /**
39
+ * Recupera la firma per un TID specifico (senza fallback).
40
+ * Restituisce solo la firma associata al TID richiesto, o undefined se non esiste.
41
+ */
42
+ static async getByTid(tid) {
43
+ const signatures = await this.getAll();
44
+ const entry = signatures.find(s => s.tid.toString() === tid.toString());
45
+ if (entry?.imgSign) {
46
+ return {
47
+ imgSign: entry.imgSign,
48
+ isDefault: tid.toString() === DEFAULT_SIGNATURE_TID.toString(),
49
+ sourceTid: tid,
50
+ method: entry.method,
51
+ signText: entry.signText
52
+ };
53
+ }
54
+ return undefined;
55
+ }
56
+ /**
57
+ * Recupera la firma per un TID specifico con fallback alla firma di default.
58
+ * Se non trova la firma per il TID specifico, prova con la firma di default (tid = -1).
59
+ * Se non esiste neanche quella, restituisce undefined.
60
+ * Restituisce anche l'informazione se è stata usata la firma di default.
61
+ */
62
+ static async getByTidWithFallback(tid) {
63
+ // Prima cerca la firma per il TID specifico
64
+ const specificResult = await this.getByTid(tid);
65
+ if (specificResult) {
66
+ return specificResult;
67
+ }
68
+ // Se non trova e il tid richiesto non è già il default, cerca la firma di default (tid = -1)
69
+ if (tid.toString() !== DEFAULT_SIGNATURE_TID.toString()) {
70
+ const defaultResult = await this.getByTid(DEFAULT_SIGNATURE_TID);
71
+ if (defaultResult) {
72
+ return {
73
+ ...defaultResult,
74
+ isDefault: true
75
+ };
76
+ }
77
+ }
78
+ return undefined;
79
+ }
80
+ /**
81
+ * Salva la firma di default valida per tutti i tipi documento (tid = 0)
82
+ * @param imgSign Immagine firma in base64
83
+ * @param method Metodo utilizzato per creare la firma
84
+ * @param signText Testo utilizzato per generare la firma (solo per method='T')
85
+ */
86
+ static async saveDefault(imgSign, method, signText) {
87
+ await this.save(DEFAULT_SIGNATURE_TID, imgSign, method, signText);
88
+ }
89
+ /**
90
+ * Salva o aggiorna la firma per un TID specifico
91
+ * @param tid ID del tipo documento
92
+ * @param imgSign Immagine firma in base64
93
+ * @param method Metodo utilizzato per creare la firma
94
+ * @param signText Testo utilizzato per generare la firma (solo per method='T')
95
+ */
96
+ static async save(tid, imgSign, method, signText) {
97
+ const signatures = await this.getAll();
98
+ const existingIndex = signatures.findIndex(s => s.tid.toString() === tid.toString());
99
+ // Il testo della firma (signText) viene salvato SOLO quando:
100
+ // 1. Il metodo di creazione è 'T' (TextEditor)
101
+ // 2. Il testo esiste ed è valorizzato
102
+ // Per i metodi 'D' (Disegno) e 'U' (Upload) il campo signText non viene mai salvato
103
+ const shouldSaveText = method === 'T' && signText;
104
+ // Pattern UPSERT: aggiorna se esiste, altrimenti crea nuova entry
105
+ if (existingIndex >= 0) {
106
+ // Aggiorna una firma esistente
107
+ signatures[existingIndex].imgSign = imgSign;
108
+ signatures[existingIndex].method = method;
109
+ if (shouldSaveText) {
110
+ // Salva il testo solo per metodo 'T' con testo valorizzato
111
+ signatures[existingIndex].signText = signText;
112
+ }
113
+ else {
114
+ // Rimuove signText se presente (es. cambio da 'T' a 'D' o 'U')
115
+ delete signatures[existingIndex].signText;
116
+ }
117
+ }
118
+ else {
119
+ // Crea una nuova entry senza includere signText di default
120
+ const entry = { tid, imgSign, method };
121
+ if (shouldSaveText) {
122
+ // Aggiunge signText solo se necessario
123
+ entry.signText = signText;
124
+ }
125
+ signatures.push(entry);
126
+ }
127
+ await SDK_Globals.tmSession?.NewUserEngine().ParamSetAsync(SIGNATURE_PARAM_KEY, JSON.stringify(signatures));
128
+ // Aggiorna la cache dopo il salvataggio
129
+ this.clearCache();
130
+ await this.getAll();
131
+ }
132
+ /**
133
+ * Elimina la firma per un TID specifico
134
+ */
135
+ static async delete(tid) {
136
+ const signatures = await this.getAll();
137
+ const filtered = signatures.filter(s => s.tid.toString() !== tid.toString());
138
+ await SDK_Globals.tmSession?.NewUserEngine().ParamSetAsync(SIGNATURE_PARAM_KEY, JSON.stringify(filtered));
139
+ // Aggiorna la cache dopo l'eliminazione
140
+ this.clearCache();
141
+ await this.getAll();
142
+ }
143
+ }
144
+ // Cache locale per evitare chiamate ripetute
145
+ SignatureParamsManager.cache = null;
@@ -9,8 +9,8 @@ interface ITMSavedQuerySelectorProps {
9
9
  manageDefault?: boolean;
10
10
  onItemClick?: (sqd: SavedQueryDescriptor) => void;
11
11
  onDeleted?: (sqd: SavedQueryDescriptor) => void;
12
- onFavoritesAdded?: (sqd: SavedQueryDescriptor) => void;
12
+ refreshFavoriteSavedQueries?: (sqd: SavedQueryDescriptor) => void;
13
13
  onRefreshData?: () => void;
14
14
  }
15
- declare const TMSavedQuerySelector: React.MemoExoticComponent<({ items, selectedId, allowShowSearch, height, manageDefault, onItemClick, onDeleted, onFavoritesAdded, onRefreshData }: ITMSavedQuerySelectorProps) => import("react/jsx-runtime").JSX.Element>;
15
+ declare const TMSavedQuerySelector: React.MemoExoticComponent<({ items, selectedId, allowShowSearch, height, manageDefault, onItemClick, onDeleted, refreshFavoriteSavedQueries, onRefreshData }: ITMSavedQuerySelectorProps) => import("react/jsx-runtime").JSX.Element>;
16
16
  export default TMSavedQuerySelector;
@@ -105,7 +105,7 @@ const getContextMenuItems = (sqd, manageDefault, isMobile, deleteAsync, setDefau
105
105
  onClick: () => { setInfoSQD?.(sqd); }
106
106
  }] : [])
107
107
  ];
108
- const TMSavedQuerySelector = React.memo(({ items, selectedId, allowShowSearch = true, height, manageDefault = true, onItemClick, onDeleted, onFavoritesAdded, onRefreshData }) => {
108
+ const TMSavedQuerySelector = React.memo(({ items, selectedId, allowShowSearch = true, height, manageDefault = true, onItemClick, onDeleted, refreshFavoriteSavedQueries, onRefreshData }) => {
109
109
  const [dataSource, setDataSource] = useState([]);
110
110
  const [selectedItem, setSelectedItem] = useState();
111
111
  const [searchText, setSearchText] = useState('');
@@ -135,6 +135,7 @@ const TMSavedQuerySelector = React.memo(({ items, selectedId, allowShowSearch =
135
135
  await SDK_Globals.tmSession?.NewSavedQueryEngine().DeleteAsync(sqd?.id);
136
136
  await loadDataAsync(true);
137
137
  onDeleted?.(sqd);
138
+ refreshFavoriteSavedQueries?.(sqd);
138
139
  TMSpinner.hide();
139
140
  }
140
141
  catch (ex) {
@@ -148,7 +149,7 @@ const TMSavedQuerySelector = React.memo(({ items, selectedId, allowShowSearch =
148
149
  try {
149
150
  TMSpinner.show();
150
151
  await SDK_Globals.tmSession?.NewSavedQueryEngine().FavoritesAddOrRemoveAsync(sqd?.id, false);
151
- onFavoritesAdded?.(sqd);
152
+ refreshFavoriteSavedQueries?.(sqd);
152
153
  TMSpinner.hide();
153
154
  ShowAlert({ mode: 'success', title: SDK_Localizator.SavedQuery, message: SDKUI_Localizator.OperationSuccess, duration: 3000 });
154
155
  }