@topconsultnpm/sdkui-react 6.20.0-dev1.43 → 6.20.0-dev1.45

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.
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useMemo, useState } from "react";
3
3
  import styled from "styled-components";
4
- import { AccessLevels, DcmtTypeListCacheService, LayoutGroupBorderStyles, LayoutGroupOrientations, LayoutItemTypes, LayoutModes, MetadataDataDomains, MetadataDataTypes, SDK_Globals, SystemMIDsAsNumber, SystemTIDs, TemplateTIDs, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
5
- import { IconUndo, IconPencil, IconFunction, IconMenuVertical, IconDataList, SDKUI_Localizator, IconNull, stringIsNullOrEmpty, deepCompare, SDKUI_Globals, IconDcmtTypeSys } from "../../helper";
4
+ import { AccessLevels, DcmtTypeListCacheService, LayoutGroupBorderStyles, LayoutGroupOrientations, LayoutItemTypes, LayoutModes, MetadataDataDomains, MetadataDataTypes, SDK_Globals, SystemMIDsAsNumber, SystemTIDs, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
5
+ import { IconUndo, IconPencil, IconFunction, IconMenuVertical, IconDataList, SDKUI_Localizator, IconNull, stringIsNullOrEmpty, deepCompare, SDKUI_Globals, IconDcmtTypeSys, isApprovalWorkflowView } from "../../helper";
6
6
  import { TMColors } from "../../utils/theme";
7
7
  import TMButton from "../base/TMButton";
8
8
  import TMDropDownMenu from "../base/TMDropDownMenu";
@@ -568,7 +568,7 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
568
568
  if (allSystem) {
569
569
  return metadataValues.map((item) => renderMetadataItem(item, true));
570
570
  }
571
- if (currentDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isReadOnly) {
571
+ if (isApprovalWorkflowView(currentDTD) && !isReadOnly) {
572
572
  return layoutWorkItem;
573
573
  }
574
574
  switch (currentDTD?.id) {
@@ -1,6 +1,18 @@
1
1
  import React from 'react';
2
2
  import { HomeBlogPost, LayoutModes, ObjectRef, SearchResultDescriptor, TaskDescriptor, ValidationItem } from '@topconsultnpm/sdk-ts';
3
3
  import { DcmtInfo, FormModes, MetadataValueDescriptorEx, TaskContext } from '../../../ts';
4
+ /**
5
+ * Definisce il contesto da cui è stato invocato il TMDcmtForm.
6
+ * Permette di gestire logiche diverse in base alla provenienza.
7
+ */
8
+ export declare enum InvocationContext {
9
+ /** Invocazione standard */
10
+ Default = "default",
11
+ /** Invocato dalla pagina Todo/Task */
12
+ Todo = "todo",
13
+ /** Invocato dalla pagina WorkflowCtrl */
14
+ WorkflowCtrl = "workflowCtrl"
15
+ }
4
16
  interface ITMDcmtFormProps {
5
17
  allTasks?: Array<TaskDescriptor>;
6
18
  getAllTasks?: () => Promise<void>;
@@ -24,7 +36,7 @@ interface ITMDcmtFormProps {
24
36
  canPrev?: boolean;
25
37
  isClosable?: boolean;
26
38
  groupId?: string;
27
- invokedByTodo?: boolean;
39
+ invocationContext?: InvocationContext;
28
40
  taskFormDialogComponent?: React.ReactNode;
29
41
  taskMoreInfo?: TaskDescriptor;
30
42
  showBackButton?: boolean;
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import TMDcmtPreview from './TMDcmtPreview';
4
- import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataTypes, ObjectClasses, ResultTypes, SDK_Globals, SDK_Localizator, SystemMIDsAsNumber, SystemTIDs, Task_States, TemplateTIDs, TID_DID, UpdateEngineByID, UserListCacheService, ValidationItem, WorkflowCacheService, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
4
+ import { AccessLevels, ArchiveConstraints, ArchiveEngineByID, DcmtTypeListCacheService, LayoutCacheService, LayoutModes, MetadataDataTypes, ObjectClasses, ResultTypes, SDK_Globals, SDK_Localizator, SystemMIDsAsNumber, SystemTIDs, Task_States, TID_DID, UpdateEngineByID, UserListCacheService, ValidationItem, WorkflowCacheService, WorkItemMetadataNames } from '@topconsultnpm/sdk-ts';
5
5
  import { WorkFlowApproveRejectPopUp, WorkFlowMoreInfoPopUp, WorkFlowOperationButtons, WorkFlowReAssignPopUp } from '../workflow/TMWorkflowPopup';
6
6
  import { DownloadTypes, FormModes, DcmtOperationTypes } from '../../../ts';
7
7
  import { DeviceType, useDeviceType } from '../../base/TMDeviceProvider';
8
8
  import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
9
9
  import { useRelatedDocuments } from '../../../hooks/useRelatedDocuments';
10
10
  import { getWorkItemSetIDAsync, handleArchiveVisibility, searchResultToMetadataValues } from '../../../helper/queryHelper';
11
- import { genUniqueId, IconShow, SDKUI_Localizator, updateMruTids, IconBoard, IconDcmtTypeSys, IconDetailDcmts, IconDownload, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, isTaskMoreInfo, IconWorkflow, IconSearch, deepCompare, IconCheck, IconActivity, TMImageLibrary, IconStar, IconRelation, IconInfo, IconArchiveDoc, IconDelete, IconPair, IconUnpair, IconArchiveMaster, IconArchiveDetail, getDcmtCicoStatus, IconFileDots, IconCustom } from '../../../helper';
11
+ import { genUniqueId, IconShow, SDKUI_Localizator, updateMruTids, IconBoard, IconDcmtTypeSys, IconDetailDcmts, IconDownload, calcIsModified, IconMenuVertical, Globalization, getListMaxItems, getSystemMetadata, IconBoxArchiveIn, IconClear, IconUndo, SDKUI_Globals, IconPreview, isTaskMoreInfo, IconWorkflow, IconSearch, deepCompare, IconCheck, IconActivity, TMImageLibrary, IconStar, IconRelation, IconInfo, IconArchiveDoc, IconDelete, IconPair, IconUnpair, IconArchiveMaster, IconArchiveDetail, getExceptionMessage, isApprovalWorkflowView, getDcmtCicoStatus, IconFileDots, IconCustom } from '../../../helper';
12
12
  import { hasDetailRelations, hasMasterRelations, isXMLFileExt } from '../../../helper/dcmtsHelper';
13
13
  import { Gutters, TMColors } from '../../../utils/theme';
14
14
  import { StyledFormButtonsContainer, StyledLoadingContainer, StyledModalContainer, StyledReferenceButton, StyledSpinner, StyledToolbarCardContainer } from '../../base/Styled';
@@ -46,9 +46,24 @@ import TMViewHistoryDcmt from '../search/TMViewHistoryDcmt';
46
46
  import TMDcmtCheckoutInfoForm from '../search/TMDcmtCheckoutInfoForm';
47
47
  import styled from 'styled-components';
48
48
  import { ContextMenu } from '../../NewComponents/ContextMenu';
49
+ //#region Interfaces, Types and Enums
50
+ /**
51
+ * Definisce il contesto da cui è stato invocato il TMDcmtForm.
52
+ * Permette di gestire logiche diverse in base alla provenienza.
53
+ */
54
+ export var InvocationContext;
55
+ (function (InvocationContext) {
56
+ /** Invocazione standard */
57
+ InvocationContext["Default"] = "default";
58
+ /** Invocato dalla pagina Todo/Task */
59
+ InvocationContext["Todo"] = "todo";
60
+ /** Invocato dalla pagina WorkflowCtrl */
61
+ InvocationContext["WorkflowCtrl"] = "workflowCtrl";
62
+ // Aggiungi qui altri contesti futuri secondo necessità
63
+ })(InvocationContext || (InvocationContext = {}));
49
64
  let abortControllerLocal = new AbortController();
50
65
  //#endregion
51
- const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, showBackButton = true, 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, onTaskCompleted, onTaskCreateRequest, inputFile = null, taskFormDialogComponent, taskMoreInfo, connectorFileSave = undefined, inputMids = [], openS4TViewer = false, onOpenS4TViewerRequest, s4TViewerDialogComponent, enableDragDropOverlay = false, passToSearch, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowButtonsRefs = false, onReferenceClick, }) => {
66
+ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTaskCallback, editTaskCallback, handleNavigateToWGs, handleNavigateToDossiers, showHeader = true, onSaveRecents, layoutMode = LayoutModes.Update, showBackButton = true, 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, invocationContext = InvocationContext.Default, titleModal, isModal = false, widthModal = "100%", heightModal = "100%", groupId, onWFOperationCompleted, onTaskCompleted, onTaskCreateRequest, inputFile = null, taskFormDialogComponent, taskMoreInfo, connectorFileSave = undefined, inputMids = [], openS4TViewer = false, onOpenS4TViewerRequest, s4TViewerDialogComponent, enableDragDropOverlay = false, passToSearch, isSharedDcmt = false, sharedSourceTID, sharedSourceDID, allowButtonsRefs = false, onReferenceClick, }) => {
52
67
  const { showHistory, showHistoryCallback, hideHistoryCallback, showCheckoutInformationForm, commentFormState, hideCommentFormCallback, showCheckoutInformationFormCallback, hideCheckoutInformationFormCallback, copyCheckoutPathToClipboardCallback, handleCheckOutCallback, handleCheckInCallback, refreshPreviewTrigger, showCicoWaitPanel, cicoWaitPanelTitle, showCicoPrimaryProgress, cicoPrimaryProgressText, cicoPrimaryProgressValue, cicoPrimaryProgressMax, } = useCheckInOutOperations();
53
68
  const [id, setID] = useState('');
54
69
  const [showWaitPanelLocal, setShowWaitPanelLocal] = useState(false);
@@ -104,6 +119,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
104
119
  const [dcmtReferences, setDcmtReferences] = useState(undefined);
105
120
  // Stato per triggerare il refresh del blog dall'esterno
106
121
  const [refreshBlogTrigger, setRefreshBlogTrigger] = useState(0);
122
+ const [wfError, setWfError] = useState(null);
107
123
  const triggerBlogRefresh = useCallback(async () => {
108
124
  setRefreshBlogTrigger(prev => prev + 1);
109
125
  }, []);
@@ -120,7 +136,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
120
136
  }, [allowButtonsRefs]);
121
137
  const { openConfirmAttachmentsDialog, ConfirmAttachmentsDialog } = useInputAttachmentsDialog();
122
138
  const { abortController, showWaitPanel, waitPanelTitle, showPrimary, waitPanelTextPrimary, waitPanelValuePrimary, waitPanelMaxValuePrimary, showSecondary, waitPanelTextSecondary, waitPanelValueSecondary, waitPanelMaxValueSecondary, downloadDcmtsAsync, runOperationAsync } = useDcmtOperations();
123
- const { workflowApproveData } = useWorkflowApprove();
139
+ const { workflowApproveData, getWorkItemsByDID } = useWorkflowApprove();
124
140
  const currentSearchResults = useMemo(() => {
125
141
  if (!formData || formData.length === 0 || !TID || !DID)
126
142
  return [];
@@ -438,74 +454,110 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
438
454
  return;
439
455
  copyCheckoutPathToClipboardCallback({ TID: currentDcmt.tid, DID: currentDcmt.did, FILEEXT: currentDcmt.fileExt }, fromDTD?.name ?? SDKUI_Localizator.SearchResult);
440
456
  };
457
+ // useEffect per il caricamento dei dati del workflow
441
458
  useEffect(() => {
459
+ // Funzione helper per caricare le informazioni del workflow
460
+ const loadWorkflowInfo = async (tid) => {
461
+ await WorkflowCacheService.GetWFInfoAsync(tid)
462
+ .then((result) => {
463
+ if (result) {
464
+ setWorkflows([result]);
465
+ setWfError(null);
466
+ }
467
+ else {
468
+ setWorkflows([]);
469
+ setWfError("Workflow info not found");
470
+ }
471
+ })
472
+ .catch(error => {
473
+ setWorkflows([]);
474
+ console.log("Error fetching workflow info:", error);
475
+ setWfError(getExceptionMessage(error));
476
+ });
477
+ };
442
478
  const loadAllWfData = async () => {
443
- if (layoutMode !== LayoutModes.Update || !DID || fromDTD?.templateTID !== TemplateTIDs.WF_WIApprView) {
479
+ // FASE 1: Validazione prerequisiti
480
+ if (layoutMode !== LayoutModes.Update || !DID) {
444
481
  setWorkItems([]);
445
482
  setWorkflows([]);
446
483
  return;
447
484
  }
448
485
  setIsWFDataLoading(true);
449
486
  try {
450
- // 1. item da processare in modo sincrono
451
- const itemsToProcess = [];
452
- for (const workflow of workflowApproveData) {
453
- for (const dataRow of workflow.dtdResult?.rows ?? []) {
454
- const did = Number(dataRow?.[1]);
455
- if (did === Number(DID)) {
456
- const tid = Number(dataRow?.[0]);
457
- itemsToProcess.push({ tid, did });
458
- }
459
- }
460
- }
487
+ // FASE 2: Raccolta work items: array di tutti i possibili work items che matchano il DID corrente
488
+ const itemsToProcess = getWorkItemsByDID(Number(DID));
489
+ // Se non ci sono work items per questo DID, resetta e esci
461
490
  if (itemsToProcess.length === 0) {
462
491
  setWorkItems([]);
463
492
  setWorkflows([]);
464
493
  setIsWFDataLoading(false);
465
494
  return;
466
495
  }
467
- // promises per i setID in modo condizionale
468
- let setIDPromises;
469
- if (workItemSetIDValue !== undefined) {
470
- // Se abbiamo il setID dal form, non facciamo chiamate di rete.
471
- setIDPromises = itemsToProcess.map(() => Promise.resolve(workItemSetIDValue));
496
+ // FASE 3: Ricerca work item specifico
497
+ // Cerca prima un match esatto con TID e DID correnti
498
+ const foundItem = itemsToProcess.find(item => item.tid === Number(TID) && item.did === Number(DID));
499
+ if (foundItem) {
500
+ // SCENARIO A: Match esatto trovato (TID e DID corrispondono)
501
+ // Il documento corrente è esattamente il work item nel workflow
502
+ let setID = undefined;
503
+ // Prova a usare il setID già presente nei metadati (più veloce)
504
+ if (workItemSetIDValue !== undefined) {
505
+ setID = workItemSetIDValue;
506
+ }
507
+ else {
508
+ // Altrimenti caricalo dal server
509
+ setID = await getWorkItemSetIDAsync(foundItem.tid, foundItem.did);
510
+ }
511
+ // Imposta un singolo work item con il setID caricato
512
+ setWorkItems([{ ...foundItem, setID }]);
513
+ // Carica le informazioni complete del workflow
514
+ await loadWorkflowInfo(foundItem.tid);
472
515
  }
473
516
  else {
474
- // Altrimenti, procediamo con le chiamate di rete come prima.
475
- setIDPromises = itemsToProcess.map(item => getWorkItemSetIDAsync(item.tid, item.did));
517
+ // SCENARIO B: Nessun match esatto, cerca solo per DID
518
+ const itemsByDID = itemsToProcess.filter(item => item.did === Number(DID));
519
+ if (itemsByDID.length === 1) {
520
+ // SCENARIO B.1: Un solo item trovato per DID
521
+ // Situazione sicura: sappiamo esattamente quale work item usare
522
+ const singleItem = itemsByDID[0];
523
+ let setID;
524
+ // Carica il setID (da cache o da server)
525
+ if (workItemSetIDValue !== undefined) {
526
+ setID = workItemSetIDValue;
527
+ }
528
+ else {
529
+ setID = await getWorkItemSetIDAsync(singleItem.tid, singleItem.did);
530
+ }
531
+ setWorkItems([{ ...singleItem, setID }]);
532
+ // Carica le informazioni del workflow
533
+ await loadWorkflowInfo(singleItem.tid);
534
+ }
535
+ else if (itemsByDID.length > 1) {
536
+ // SCENARIO B.2: Più item trovati per lo stesso DID
537
+ // Ambiguità: lo stesso documento appare in più workflow diversi
538
+ const finalWorkItems = itemsByDID.map((item, index) => ({ ...item, setID: undefined }));
539
+ setWorkItems(finalWorkItems);
540
+ setWorkflows([]); // Non carichiamo il workflow in caso di ambiguità
541
+ }
476
542
  }
477
- // Crea un array di Promise per tutte le chiamate a GetWFInfoAsync
478
- const workflowInfoPromises = itemsToProcess.map(item => WorkflowCacheService.GetWFInfoAsync(item.tid));
479
- // Esegui tutte le chiamate in parallelo e attendi i risultati
480
- const setIDResults = await Promise.all(setIDPromises);
481
- const workflowInfoResults = await Promise.all(workflowInfoPromises);
482
- // Combina i risultati
483
- const finalWorkItems = itemsToProcess.map((item, index) => ({
484
- ...item,
485
- setID: setIDResults[index],
486
- }));
487
- const validWorkflows = workflowInfoResults.filter(Boolean);
488
- // Aggiorna lo stato una sola volta con i dati finali
489
- setWorkItems(finalWorkItems);
490
- setWorkflows(validWorkflows);
491
543
  }
492
544
  catch (error) {
545
+ // FASE 4: Gestione errori
493
546
  TMExceptionBoxManager.show({ exception: error });
494
547
  setWorkItems([]);
495
548
  setWorkflows([]);
496
549
  }
497
550
  finally {
551
+ // Garantisce sempre il reset del flag di loading
498
552
  setIsWFDataLoading(false);
499
553
  }
500
554
  };
501
- // Usa hasFormData invece di formDataRef.current
502
555
  if (!hasFormData || !fromDTD?.id) {
503
- // console.log("formData is empty or fromDTD not loaded, skipping loadAllWfData");
504
556
  return;
505
557
  }
506
558
  if (workItemSetIDValue !== undefined || workflowApproveData.length > 0)
507
559
  loadAllWfData();
508
- }, [hasFormData, workItemSetIDValue, workflowApproveData, DID, layoutMode, fromDTD?.templateTID, fromDTD?.id]);
560
+ }, [hasFormData, workItemSetIDValue, workflowApproveData, getWorkItemsByDID, TID, DID, layoutMode, fromDTD?.templateTID, fromDTD?.id]);
509
561
  const getSelectionDcmtInfo = useCallback(() => {
510
562
  let dcmts = [];
511
563
  dcmts.push({ TID: TID ?? 0, DID: DID ?? 0 });
@@ -536,7 +588,11 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
536
588
  const showToppyForCompleteMoreInfo = useMemo(() => layoutMode === LayoutModes.Update && !!isTaskMoreInfo(taskMoreInfo?.name) && taskMoreInfo?.state !== Task_States.Completed, [layoutMode, taskMoreInfo?.name, taskMoreInfo?.state]);
537
589
  const showToppyForReferences = useMemo(() => allowButtonsRefs && layoutMode === LayoutModes.Update && !!(dcmtReferences && dcmtReferences.length > 0) && !isOpenDetails && !isOpenMaster, [allowButtonsRefs, layoutMode, dcmtReferences, isOpenDetails, isOpenMaster]);
538
590
  const isMobile = useMemo(() => deviceType === DeviceType.MOBILE, [deviceType]);
539
- const isApprView = useMemo(() => fromDTD?.templateTID === TemplateTIDs.WF_WIApprView, [fromDTD?.templateTID]);
591
+ const isApprView = useMemo(() => {
592
+ if (!fromDTD)
593
+ return false;
594
+ return isApprovalWorkflowView(fromDTD);
595
+ }, [fromDTD?.id]);
540
596
  const workitemSetID = useMemo(() => workItems.find(o => o.did === Number(DID))?.setID || formData.find(o => o.md?.name === WorkItemMetadataNames.WI_SetID)?.value, [workItems, DID, formData]);
541
597
  const approvalVID = useMemo(() => workItems.length > 0 ? Number(workItems[0].tid) : -1, [workItems]);
542
598
  //here
@@ -571,11 +627,12 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
571
627
  disabled: false,
572
628
  onClick: () => {
573
629
  const dcmt = getDcmts()[0];
630
+ const name = `${fromDTD?.name ?? '-'} (DID: ${dcmt.DID})`;
574
631
  const taskContext = {
575
632
  document: {
576
633
  tid: dcmt.TID,
577
634
  did: dcmt.DID,
578
- name: fromDTD?.description || ''
635
+ name: name
579
636
  }
580
637
  };
581
638
  onTaskCreateRequest(taskContext);
@@ -1167,7 +1224,27 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
1167
1224
  fontSize: '1.1rem',
1168
1225
  color: TMColors.primaryColor,
1169
1226
  textAlign: 'center',
1170
- }, children: SDKUI_Localizator.WorkflowDiagramNotAuthorized }))
1227
+ display: 'flex',
1228
+ flexDirection: 'column',
1229
+ alignItems: 'center',
1230
+ gap: '12px',
1231
+ }, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [_jsx("span", { children: SDKUI_Localizator.WorkflowDiagramNotAuthorized }), wfError && (_jsx(TMTooltip, { content: 'Visualizza errore', children: _jsx("i", { className: "dx-icon-info", style: { fontSize: 20, cursor: 'pointer', color: '#dc3545' }, onClick: () => {
1232
+ TMMessageBoxManager.show({
1233
+ title: 'Dettagli Errore Workflow',
1234
+ initialWidth: !isMobile ? '700px' : undefined,
1235
+ message: (_jsx("pre", { style: {
1236
+ whiteSpace: 'pre-wrap',
1237
+ background: '#f5f5f5',
1238
+ padding: '12px',
1239
+ borderRadius: '6px',
1240
+ userSelect: 'text',
1241
+ cursor: 'text',
1242
+ }, children: wfError })),
1243
+ resizable: true,
1244
+ showToppy: false,
1245
+ buttons: [ButtonNames.OK],
1246
+ });
1247
+ } }) }))] }) }))
1171
1248
  : _jsx("div", { style: {
1172
1249
  position: 'absolute',
1173
1250
  top: '50%',
@@ -1307,7 +1384,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
1307
1384
  // Checks if there's a saved panel layout for the current context (ToDo or general)
1308
1385
  const hasSavedLayout = () => {
1309
1386
  const { setting } = getCurrentDcmtFormSetting();
1310
- if (invokedByTodo) {
1387
+ if (invocationContext === InvocationContext.Todo) {
1311
1388
  // If invoked by ToDo, check for existence of layoutToDo and that it has keys
1312
1389
  return setting.layoutToDo !== undefined && Object.keys(setting.layoutToDo).length > 0;
1313
1390
  }
@@ -1326,8 +1403,8 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
1326
1403
  // Prepare the new setting object with updated layout depending on context
1327
1404
  const newSetting = {
1328
1405
  TID: normalizedTID,
1329
- layout: invokedByTodo ? (existingSetting.layout ?? {}) : state,
1330
- layoutToDo: invokedByTodo ? state : (existingSetting.layoutToDo ?? {}),
1406
+ layout: invocationContext === InvocationContext.Todo ? (existingSetting.layout ?? {}) : state,
1407
+ layoutToDo: invocationContext === InvocationContext.Todo ? state : (existingSetting.layoutToDo ?? {}),
1331
1408
  };
1332
1409
  // Replace existing setting if found, otherwise push a new one
1333
1410
  if (idx >= 0) {
@@ -1346,7 +1423,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
1346
1423
  return undefined;
1347
1424
  const settings = getCurrentDcmtFormSetting()?.setting;
1348
1425
  // Return the appropriate layout based on context
1349
- return invokedByTodo ? settings?.layoutToDo : settings?.layout;
1426
+ return invocationContext === InvocationContext.Todo ? settings?.layoutToDo : settings?.layout;
1350
1427
  };
1351
1428
  const handleCompleteMoreInfo = useCallback(async () => {
1352
1429
  try {
@@ -1427,7 +1504,7 @@ const TMDcmtForm = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, addTa
1427
1504
  } }), 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) }), showMoreInfoPopup && _jsx(WorkFlowMoreInfoPopUp, { deviceType: deviceType, onCompleted: handleWFOperationCompleted, TID: approvalVID, DID: DID, onClose: () => setShowMoreInfoPopup(false) }), (isModal && onClose) && _jsx("div", { id: "TMDcmtFormShowConfirmForClose-" + id })] }) }), _jsx(ToppyDraggableHelpCenter, { initialIsCollapsed: false, deviceType: deviceType, isVisible: (showToppyForApprove || showToppyForCompleteMoreInfo || showToppyForReferences) && !openS4TViewer, content: _jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '10px' }, children: [showToppyForApprove && (workItems.length === 1 ?
1428
1505
  _jsx(WorkFlowOperationButtons, { deviceType: deviceType, onApprove: () => setShowApprovePopup(true), onSignApprove: handleSignApprove, onReject: () => setShowRejectPopup(true), onReAssign: () => setShowReAssignPopup(true), onMoreInfo: () => setShowMoreInfoPopup(true), dtd: fromDTD })
1429
1506
  :
1430
- _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.`] })), showToppyForCompleteMoreInfo && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: `${SDKUI_Localizator.MoreInfoCompleteRequestSentBy} ${taskMoreInfo?.fromName}!` }), _jsx(TMButton, { caption: SDKUI_Localizator.CommentAndComplete, color: 'success', showTooltip: false, onClick: () => {
1507
+ _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".`] })), showToppyForCompleteMoreInfo && (_jsxs(_Fragment, { children: [_jsx("div", { style: { padding: 10, color: 'white', maxWidth: '180px', borderRadius: 10, background: '#1B1464 0% 0% no-repeat padding-box', border: '1px solid #FFFFFF' }, children: `${SDKUI_Localizator.MoreInfoCompleteRequestSentBy} ${taskMoreInfo?.fromName}!` }), _jsx(TMButton, { caption: SDKUI_Localizator.CommentAndComplete, color: 'success', showTooltip: false, onClick: () => {
1431
1508
  setShowCommentForm(true);
1432
1509
  } })] })), (showToppyForReferences && (dcmtReferences && dcmtReferences.length > 0)) && dcmtReferences
1433
1510
  .filter(ref => ref.objClass === ObjectClasses.Dossier || ref.objClass === ObjectClasses.WorkingGroup) // keep only known objClass types
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
- import { SDK_Globals, DataColumnTypes, MetadataDataDomains, DataListViewModes, MetadataFormats, LayoutModes, TemplateTIDs, DcmtTypeListCacheService, SystemMIDsAsNumber, RetrieveFileOptions, DcmtOpers, GeneralRetrieveFormats, AccessLevelsEx, LayoutCacheService, UserListCacheService } from '@topconsultnpm/sdk-ts';
3
+ import { SDK_Globals, DataColumnTypes, MetadataDataDomains, DataListViewModes, MetadataFormats, LayoutModes, DcmtTypeListCacheService, SystemMIDsAsNumber, RetrieveFileOptions, DcmtOpers, GeneralRetrieveFormats, AccessLevelsEx, LayoutCacheService, UserListCacheService } from '@topconsultnpm/sdk-ts';
4
4
  import styled from 'styled-components';
5
5
  import { getAllFieldSelectedDcmtsOrFocused, getCommandsMenuItems, getSelectedDcmtsOrFocused } from './TMSearchResultsMenuItems';
6
- import { genUniqueId, IconShow, IconBoard, IconDcmtTypeSys, SDKUI_Localizator, IconDelete, IconRefresh, IconMenuVertical, deepCompare, generateUniqueColumnKeys, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, IconSearchCheck, TMImageLibrary, convertSearchResultDescriptorToFileItems, IconCustom } from '../../../helper';
6
+ import { genUniqueId, IconShow, IconBoard, IconDcmtTypeSys, SDKUI_Localizator, IconDelete, IconRefresh, IconMenuVertical, deepCompare, generateUniqueColumnKeys, searchResultDescriptorToSimpleArray, searchResultToMetadataValues, IconSearchCheck, TMImageLibrary, convertSearchResultDescriptorToFileItems, IconCustom, isApprovalWorkflowView } from '../../../helper';
7
7
  import { useDcmtOperations } from '../../../hooks/useDcmtOperations';
8
8
  import { useInputAttachmentsDialog, useInputCvtFormatDialog } from '../../../hooks/useInputDialog';
9
9
  import { useRelatedDocuments } from '../../../hooks/useRelatedDocuments';
@@ -261,7 +261,7 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
261
261
  try {
262
262
  const dtd = await DcmtTypeListCacheService.GetWithNotGrantedAsync(item.TID, item?.DID);
263
263
  if (dtd) {
264
- const isWorkItem = dtd?.templateTID === TemplateTIDs.WF_WIApprView;
264
+ const isWorkItem = isApprovalWorkflowView(dtd);
265
265
  const name = `${dtd.name ?? '-'} (DID: ${item.DID})`;
266
266
  onTaskCreateRequest?.(isWorkItem
267
267
  ? { workItem: { tid: item.TID, did: item.DID, name } }
@@ -582,7 +582,9 @@ const TMSearchResult = ({ allTasks = [], getAllTasks, deleteTaskByIdsCallback, a
582
582
  const handleSavedAsyncCallback = useCallback(async (tid, did, metadataResult) => {
583
583
  await refreshFocusedDataRowAsync(tid, did, true, metadataResult);
584
584
  }, [refreshFocusedDataRowAsync]);
585
- const showToppyForApprove = (isVisible && fromDTD?.templateTID === TemplateTIDs.WF_WIApprView && !isOpenDcmtForm && !isOpenDetails && !isOpenMaster);
585
+ const showToppyForApprove = useMemo(() => {
586
+ return Boolean(isVisible && fromDTD && isApprovalWorkflowView(fromDTD) && !isOpenDcmtForm && !isOpenDetails && !isOpenMaster);
587
+ }, [isVisible, fromDTD, isOpenDcmtForm, isOpenDetails, isOpenMaster]);
586
588
  const floatingMenuItems = useMemo(() => {
587
589
  return getCommandsMenuItems(isMobile, fromDTD, allUsers, selectedItems, focusedItem, context, showFloatingBar, workingGroupContext, showSearch, setShowFloatingBar, openFormHandler, openSharedArchiveHandler, showSharedDcmtsHandler, downloadDcmtsAsync, runOperationAsync, onRefreshSearchAsync, refreshSelectionDataRowsAsync, onRefreshAfterAddDcmtToFavs, confirmFormat, openConfirmAttachmentsDialog, openTaskFormHandler, openDetailDcmtsFormHandler, openMasterDcmtsFormHandler, openBatchUpdateFormHandler, openExportForm, handleToggleSearch, handleSignApprove, openSignSettingsForm, handleCheckOutOperationCallback, handleCheckInOperationCallback, showCheckoutInformationFormCallback, showHistoryCallback, copyCheckoutPathToClipboardOperationCallback, openWGsCopyMoveForm, openCommentFormCallback, openEditPdf, openAddDocumentForm, passToArchiveCallback, archiveMasterDocuments, archiveDetailDocuments, currentTIDHasMasterRelations, currentTIDHasDetailRelations, canArchiveMasterRelation, canArchiveDetailRelation, pairManyToMany, hasManyToManyRelation).concat([customButtonMenuItems()]);
588
590
  }, [
@@ -1656,10 +1656,10 @@ const WFDiagram = ({ xmlDiagramString, currentSetID, allowEdit = true, onDiagram
1656
1656
  }
1657
1657
  }, [wfDiagram]);
1658
1658
  const handleCanvasDoubleClick = useCallback((event) => {
1659
- if (isReadOnly) {
1659
+ if (isReadOnly && allowEdit) {
1660
1660
  toggleReadOnlyMode();
1661
1661
  }
1662
- }, [isReadOnly, toggleReadOnlyMode]);
1662
+ }, [isReadOnly, allowEdit, toggleReadOnlyMode]);
1663
1663
  const handleFullScreenKeyDown = useCallback((event) => {
1664
1664
  if (event.key === 'Escape') {
1665
1665
  // Blocca sempre la propagazione per evitare che TMSaveForm riceva l'evento
@@ -112,5 +112,6 @@ export * from "./features/workflow/diagram/WFDiagram";
112
112
  export * from "./features/workflow/diagram/workflowHelpers";
113
113
  export * from "./features/workflow/diagram/xmlParser";
114
114
  export * from "./features/workflow/diagram/interfaces";
115
+ export * from "./features/workflow/diagram/DiagramItemSvgContent";
115
116
  export { default as TMWizard } from './wizard/TMWizard';
116
117
  export * from './wizard/TMWizard';
@@ -133,6 +133,7 @@ export * from "./features/workflow/diagram/WFDiagram";
133
133
  export * from "./features/workflow/diagram/workflowHelpers";
134
134
  export * from "./features/workflow/diagram/xmlParser";
135
135
  export * from "./features/workflow/diagram/interfaces";
136
+ export * from "./features/workflow/diagram/DiagramItemSvgContent";
136
137
  // wizard
137
138
  export { default as TMWizard } from './wizard/TMWizard';
138
139
  export * from './wizard/TMWizard';
@@ -1,6 +1,6 @@
1
1
  import { Colors as ColorsType } from "../components/base/TMEditorBase";
2
2
  import { DeviceType } from "../components";
3
- import { AppModules, DataColumnDescriptor, ITopMediaSession, MetadataDescriptor, QueryDescriptor, SearchResultDescriptor } from "@topconsultnpm/sdk-ts";
3
+ import { AppModules, DataColumnDescriptor, DcmtTypeDescriptor, ITopMediaSession, MetadataDescriptor, QueryDescriptor, SearchResultDescriptor } from "@topconsultnpm/sdk-ts";
4
4
  import { FileExtensionHandler, FormModes, moduleTypes } from "../ts";
5
5
  declare const TABLET_WIDTH = 1024;
6
6
  declare const MOBILE_WIDTH = 640;
@@ -89,3 +89,4 @@ export declare class AreaHelper {
89
89
  static ExtractAreaInfo_1(areaPath: string): AreaValues;
90
90
  static ExtractAreaInfo_2(areaPath: string, extractFileName: boolean): AreaValues;
91
91
  }
92
+ export declare const isApprovalWorkflowView: (dtd: DcmtTypeDescriptor) => boolean;
@@ -1,7 +1,7 @@
1
1
  import { Fragment as _Fragment, jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Colors } from "../utils/theme";
3
3
  import { ButtonNames, DeviceType, TMExceptionBoxManager, TMMessageBoxManager, TMSpinner } from "../components";
4
- import { AccessLevels, MetadataDataDomains, MetadataDataTypes, MetadataDescriptor, MetadataFormatDescriptor, MetadataFormats, MetadataPermission, SDK_Globals, SetGlobalsInfoAsync, SystemMIDs, SystemMIDsAsNumber, TopMediaServer } from "@topconsultnpm/sdk-ts";
4
+ import { AccessLevels, MetadataDataDomains, MetadataDataTypes, MetadataDescriptor, MetadataFormatDescriptor, MetadataFormats, MetadataPermission, SDK_Globals, SetGlobalsInfoAsync, SystemMIDs, SystemMIDsAsNumber, TopMediaServer, WorkItemMetadataNames } from "@topconsultnpm/sdk-ts";
5
5
  import { Buffer } from 'buffer';
6
6
  import { buildTypes, FileExtensionHandler, FormModes, moduleTypes } from "../ts";
7
7
  import { SDKUI_Localizator } from "./SDKUI_Localizator";
@@ -856,3 +856,7 @@ export class AreaHelper {
856
856
  }
857
857
  AreaHelper.AreaPathPrefix = "tmarea:\\\\";
858
858
  AreaHelper.AreaFolderNamePrefix = "AID_";
859
+ export const isApprovalWorkflowView = (dtd) => {
860
+ return Boolean(dtd?.isView &&
861
+ dtd.metadata?.some(data => data.name === WorkItemMetadataNames.WI_DID));
862
+ };
@@ -8,4 +8,8 @@ export declare const useWorkflowApprove: () => {
8
8
  isLoading: boolean;
9
9
  refreshWorkflowApprove: () => Promise<void>;
10
10
  totalDcmtsFound: number;
11
+ getWorkItemsByDID: (targetDID: number) => {
12
+ tid: number;
13
+ did: number;
14
+ }[];
11
15
  };
@@ -14,6 +14,19 @@ export const useWorkflowApprove = () => {
14
14
  const calculateTotalDcmtsFound = useCallback((data) => {
15
15
  return data.reduce((sum, item) => sum + (item.dcmtsFound || 0), 0);
16
16
  }, []);
17
+ /**
18
+ * Ottiene tutti i work items che corrispondono a un determinato DID.
19
+ * Restituisce un array di oggetti { tid, did }.
20
+ */
21
+ const getWorkItemsByDID = useCallback((targetDID) => {
22
+ return workflowApproveData
23
+ .flatMap(workflow => workflow.dtdResult?.rows ?? [])
24
+ .filter(dataRow => Number(dataRow?.[1]) === targetDID)
25
+ .map(dataRow => ({
26
+ tid: Number(dataRow[0]),
27
+ did: targetDID
28
+ }));
29
+ }, [workflowApproveData]);
17
30
  /**
18
31
  * Esegue il fetch dei dati dal workflow, aggiorna lo stato globale
19
32
  * e notifica gli altri componenti tramite un evento.
@@ -53,5 +66,5 @@ export const useWorkflowApprove = () => {
53
66
  window.removeEventListener('onWorkflowApproveChange', handleUpdate);
54
67
  };
55
68
  }, [calculateTotalDcmtsFound]);
56
- return { workflowApproveData, isLoading, refreshWorkflowApprove, totalDcmtsFound };
69
+ return { workflowApproveData, isLoading, refreshWorkflowApprove, totalDcmtsFound, getWorkItemsByDID };
57
70
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.20.0-dev1.43",
3
+ "version": "6.20.0-dev1.45",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",