@topconsultnpm/sdkui-react 6.19.0-dev1.44 → 6.19.0-dev1.46

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.
@@ -402,38 +402,56 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
402
402
  return (_jsx("div", { style: { width: '100%' }, children: dsAttachsData.length > 0 && _jsx(TMAccordion, { title: SDKUI_Localizator.Attachment, children: dsAttachsData.map(item => renderMetadataItem(item, isReadOnly)) }) }));
403
403
  }, [metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
404
404
  const layoutCustom = useMemo(() => {
405
+ console.log('Rendering custom layout with layout:', layout);
405
406
  if (!layout || !layout.items || layout.items.length === 0) {
406
407
  return metadataValues.map((item) => renderMetadataItem(item));
407
408
  }
408
- // Build a map of items by ID for quick lookup
409
+ // Build a map of items by ID for quick lookup (include negative/zero IDs)
409
410
  const itemsById = new Map();
410
411
  layout.items.forEach(item => {
411
- if (item.layoutItemID) {
412
+ if (item.layoutItemID !== undefined && item.layoutItemID !== null) {
412
413
  itemsById.set(item.layoutItemID, item);
413
414
  }
414
415
  });
415
- // Find root items (items without parent or parent === 0)
416
- const rootItems = layout.items.filter(item => item.type === LayoutItemTypes.LayoutRoot);
417
- // Recursive function to get children of an item
416
+ // Determine root items. Prefer explicit LayoutRoot items if present;
417
+ // otherwise fall back to items with no parent (parentID undefined or -1).
418
+ const hasExplicitRoot = layout.items.some(item => item.type === LayoutItemTypes.LayoutRoot);
419
+ const rootItems = hasExplicitRoot
420
+ ? layout.items.filter(item => item.type === LayoutItemTypes.LayoutRoot)
421
+ : layout.items.filter(item => item.parentID === undefined || item.parentID === -1);
422
+ // Recursive function to get children of an item. Handle parentID === -1 and undefined
423
+ // because some layouts use -1 for root while children use undefined.
418
424
  const getChildren = (parentID) => {
425
+ if (parentID === -1) {
426
+ return layout.items?.filter(item => item.parentID === -1 || item.parentID === undefined) || [];
427
+ }
428
+ if (parentID === undefined) {
429
+ return layout.items?.filter(item => item.parentID === undefined) || [];
430
+ }
419
431
  return layout.items?.filter(item => item.parentID === parentID) || [];
420
432
  };
421
433
  // Recursive function to render layout items with depth tracking for indentation
422
- const renderLayoutItem = (layoutItem, depth = 0) => {
434
+ // Prevent infinite recursion by tracking visited layoutItemIDs (handles malformed layouts where an item
435
+ // may reference itself as a child or cycles exist).
436
+ const renderLayoutItem = (layoutItem, depth = 0, visited = new Set()) => {
437
+ const id = layoutItem.layoutItemID ?? 0;
438
+ if (visited.has(id))
439
+ return null;
440
+ visited.add(id);
423
441
  // Check if this is a LayoutRoot - just render its children
424
442
  if (layoutItem.type === LayoutItemTypes.LayoutRoot) {
425
- const children = getChildren(layoutItem.layoutItemID ?? 0);
426
- return (_jsx(React.Fragment, { children: children.map(child => renderLayoutItem(child, depth)) }, `root-${layoutItem.layoutItemID}`));
443
+ const children = getChildren(layoutItem.layoutItemID);
444
+ return (_jsx(React.Fragment, { children: children.map(child => renderLayoutItem(child, depth, visited)) }, `root-${layoutItem.layoutItemID}`));
427
445
  }
428
446
  // Check if this is a LayoutGroup
429
447
  else if (layoutItem.type === LayoutItemTypes.LayoutGroup && layoutItem.lgd) {
430
- const children = getChildren(layoutItem.layoutItemID ?? 0);
448
+ const children = getChildren(layoutItem.layoutItemID);
431
449
  const groupDescriptor = layoutItem.lgd;
432
450
  const groupTitle = groupDescriptor.caption || `Group ${layoutItem.layoutItemID}`;
433
451
  const isCollapsed = false; // LayoutGroupDescriptor doesn't have collapsed property
434
452
  // Apply indentation only to subgroups (depth > 0), not to root groups
435
453
  const indentationPx = depth > 0 ? depth * 10 : 0;
436
- return (_jsx("div", { style: { paddingLeft: `${indentationPx}px` }, children: _jsx(TMAccordion, { title: groupTitle, defaultCollapsed: isCollapsed, children: children.map(child => renderLayoutItem(child, depth + 1)) }) }, `group-wrapper-${layoutItem.layoutItemID}`));
454
+ return (_jsx("div", { style: { paddingLeft: `${indentationPx}px` }, children: _jsx(TMAccordion, { title: groupTitle, defaultCollapsed: isCollapsed, children: children.map(child => renderLayoutItem(child, depth + 1, visited)) }) }, `group-wrapper-${layoutItem.layoutItemID}`));
437
455
  }
438
456
  // Check if this is a LayoutControlItem (metadata field)
439
457
  else if (layoutItem.type === LayoutItemTypes.LayoutControlItem && layoutItem.lcid) {
@@ -455,7 +473,10 @@ const TMMetadataValues = ({ showCheckBoxes = ShowCheckBoxesMode.Never, checkPerm
455
473
  }
456
474
  return null;
457
475
  };
458
- return (_jsx("div", { style: { width: '100%' }, children: rootItems.map(item => renderLayoutItem(item, 0)) }));
476
+ return (_jsx("div", { style: { width: '100%' }, children: (() => {
477
+ const visited = new Set();
478
+ return rootItems.map(item => renderLayoutItem(item, 0, visited));
479
+ })() }));
459
480
  }, [layout, metadataValues, showCheckBoxes, showNullValueCheckBoxes, isReadOnly, dynDataListsToBeRefreshed, validationItems, selectedMID, isOpenDistinctValues, openChooserBySingleClick, metadataValuesOrig]);
460
481
  const renderForm = useMemo(() => {
461
482
  // Se currentDTD non è ancora stato caricato, non renderizzare nulla
@@ -30,7 +30,10 @@ const TMArchive = ({ onDcmtTypeSelect = undefined, inputTID, inputFile = null, c
30
30
  }, [inputTID, mruTIDs]);
31
31
  useEffect(() => {
32
32
  pendingMidsRef.current = inputMids;
33
- }, [inputMids]);
33
+ if (currentTID && currentTID > 0 && inputMids && inputMids.length > 0) {
34
+ setCurrentInputMids(inputMids);
35
+ }
36
+ }, [inputMids, currentTID]);
34
37
  useEffect(() => {
35
38
  if (!currentTID || currentTID <= 0) {
36
39
  previousTIDRef.current = currentTID;
@@ -42,20 +45,16 @@ const TMArchive = ({ onDcmtTypeSelect = undefined, inputTID, inputFile = null, c
42
45
  setFromDTD(dtd);
43
46
  });
44
47
  if (previousTIDRef.current !== undefined && previousTIDRef.current > 0 && previousTIDRef.current !== currentTID) {
48
+ if (!isSharedArchive) {
49
+ setCurrentInputMids([]);
50
+ }
45
51
  if (pendingMidsRef.current && pendingMidsRef.current.length > 0) {
46
52
  setCurrentInputMids(pendingMidsRef.current);
47
53
  pendingMidsRef.current = null;
48
54
  }
49
- else {
50
- setCurrentInputMids([]);
51
- }
52
- }
53
- else if (pendingMidsRef.current) {
54
- setCurrentInputMids(pendingMidsRef.current);
55
- pendingMidsRef.current = null;
56
55
  }
57
56
  previousTIDRef.current = currentTID;
58
- }, [currentTID, onDcmtTypeSelect]);
57
+ }, [currentTID, onDcmtTypeSelect, isSharedArchive]);
59
58
  const isMobile = deviceType === DeviceType.MOBILE;
60
59
  const tmTreeSelectorElement = useMemo(() => _jsx(TMTreeSelectorWrapper, { isMobile: isMobile, isSharedArchive: isSharedArchive, onSelectedTIDChanged: (tid) => {
61
60
  setCurrentTID(tid);
@@ -73,10 +72,11 @@ const TMArchive = ({ onDcmtTypeSelect = undefined, inputTID, inputFile = null, c
73
72
  setMruTIDs(newMruTIDS);
74
73
  } }), [mruTIDs, currentMruTID, deviceType, isSharedArchive]);
75
74
  const tmFormElement = useMemo(() => currentTID ?
76
- _jsx(TMDcmtForm, { TID: currentTID, DID: currentTID === inputTID ? inputDID : undefined, sharedSourceTID: isSharedArchive ? inputTID : undefined, sharedSourceDID: isSharedArchive ? inputDID : undefined, groupId: 'tmForm', layoutMode: LayoutModes.Ark, onClose: deviceType === DeviceType.MOBILE ? () => setCurrentTID(undefined) : undefined, onSaveRecents: (TIDs) => setMruTIDs(TIDs), showDcmtFormSidebar: false, inputFile: inputFile, connectorFileSave: connectorFileSave, onSavedAsyncCallback: onSavedAsyncCallback, inputMids: currentInputMids, enableDragDropOverlay: enableDragDropOverlay, passToSearch: passToSearch ? (outputMids) => {
75
+ _jsx(TMDcmtForm, { TID: currentTID, DID: currentTID === inputTID ? inputDID : undefined, sharedSourceTID: isSharedArchive ? inputTID : undefined, sharedSourceDID: isSharedArchive ? inputDID : undefined, groupId: 'tmForm', layoutMode: LayoutModes.Ark, onClose: deviceType === DeviceType.MOBILE ? () => setCurrentTID(undefined) : undefined, onSaveRecents: (TIDs) => setMruTIDs(TIDs), showDcmtFormSidebar: false, inputFile: inputFile, connectorFileSave: connectorFileSave, onSavedAsyncCallback: onSavedAsyncCallback, inputMids: currentInputMids, enableDragDropOverlay: enableDragDropOverlay, passToSearch: passToSearch ? (outputMids, tid) => {
76
+ const tidToUse = tid ?? currentTID;
77
77
  if (onDcmtTypeSelect)
78
- onDcmtTypeSelect(currentTID);
79
- passToSearch(currentTID, outputMids);
78
+ onDcmtTypeSelect(tidToUse);
79
+ passToSearch(tidToUse, outputMids);
80
80
  } : undefined, isSharedDcmt: isSharedArchive }, currentTID)
81
81
  :
82
82
  _jsx(TMPanel, { title: 'Archiviazione', allowMaximize: false, children: _jsxs(TMLayoutContainer, { gap: 30, alignItems: 'center', justifyContent: 'center', children: [_jsx(StyledToppyTextContainer, { children: _jsx(StyledToppyText, { children: SDKUI_Localizator.DcmtTypeSelect }) }), _jsx(StyledToppyImage, { src: Logo, alt: 'Toppy' })] }) }), [currentTID, deviceType, mruTIDs, inputFile, currentInputMids, enableDragDropOverlay, isSharedArchive]);
@@ -44,7 +44,7 @@ interface ITMDcmtFormProps {
44
44
  passToSearch?: (outputMids: Array<{
45
45
  mid: number;
46
46
  value: string;
47
- }>) => void;
47
+ }>, tid?: number) => void;
48
48
  isSharedDcmt?: boolean;
49
49
  sharedSourceTID?: number;
50
50
  sharedSourceDID?: number;
@@ -291,6 +291,7 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
291
291
  if (layoutMode !== LayoutModes.Ark)
292
292
  return;
293
293
  setFocusedMetadataValue(undefined);
294
+ appliedInputMidsRef.current = null;
294
295
  }, [fromDTD, layoutMode]);
295
296
  useEffect(() => {
296
297
  if (!inputMids || inputMids.length === 0)
@@ -421,8 +422,8 @@ const TMDcmtForm = ({ showHeader = true, onSaveRecents, layoutMode = LayoutModes
421
422
  const outputMids = formData
422
423
  .filter(md => md.mid && md.mid > 100 && md.value && md.value.length > 0)
423
424
  .map(md => ({ mid: md.mid, value: md.value }));
424
- passToSearch(outputMids);
425
- }, [passToSearch, formData]);
425
+ passToSearch(outputMids, TID);
426
+ }, [passToSearch, formData, TID]);
426
427
  const isPreviewDisabled = useMemo(() => layoutMode === LayoutModes.Ark && fromDTD?.archiveConstraint === ArchiveConstraints.OnlyMetadata, [layoutMode, fromDTD?.archiveConstraint]);
427
428
  const isBoardDisabled = useMemo(() => layoutMode !== LayoutModes.Update || fromDTD?.hasBlog !== 1, [layoutMode, fromDTD?.hasBlog]);
428
429
  const isSysMetadataDisabled = useMemo(() => layoutMode !== LayoutModes.Update, [layoutMode]);
@@ -19,7 +19,7 @@ interface ITMSearchQueryPanelProps {
19
19
  passToArchiveCallback?: (outputMids: Array<{
20
20
  mid: number;
21
21
  value: string;
22
- }>) => void;
22
+ }>, tid?: number) => void;
23
23
  }
24
24
  declare const TMSearchQueryPanel: React.FunctionComponent<ITMSearchQueryPanelProps>;
25
25
  export default TMSearchQueryPanel;
@@ -52,7 +52,24 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
52
52
  pendingMidsRef.current = inputMids ?? null;
53
53
  }, [inputMids]);
54
54
  useEffect(() => {
55
- if (!qd || !fromDTD)
55
+ if (!fromDTD)
56
+ return;
57
+ // Reset appliedInputMidsRef when TID changes so pending mids can be applied to new TID
58
+ appliedInputMidsRef.current = null;
59
+ const initQd = async () => {
60
+ // Only initialize if qd doesn't exist or is for a different TID
61
+ if (!qd || qd.from?.tid !== fromDTD.id) {
62
+ const newQd = await getQD(fromDTD.id, false);
63
+ if (newQd) {
64
+ setQd(newQd);
65
+ }
66
+ }
67
+ };
68
+ initQd();
69
+ }, [fromDTD?.id]);
70
+ // Apply inputMids when qd is ready and matches fromDTD
71
+ useEffect(() => {
72
+ if (!qd || !fromDTD || qd.from?.tid !== fromDTD.id)
56
73
  return;
57
74
  const midsToApply = pendingMidsRef.current;
58
75
  if (!midsToApply || midsToApply.length === 0)
@@ -67,6 +84,11 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
67
84
  }) || [];
68
85
  midsToApply.forEach(im => {
69
86
  const md = fromDTD.metadata?.find(m => m.id === im.mid);
87
+ // Skip MIDs that don't belong to this TID
88
+ if (!md) {
89
+ console.warn(`MID ${im.mid} does not belong to TID ${fromDTD.id}, skipping`);
90
+ return;
91
+ }
70
92
  const defaultOperator = getDefaultOperator(md?.dataDomain, md?.dataType);
71
93
  let existingWi = newWhere.find(wi => wi.mid === im.mid);
72
94
  if (existingWi) {
@@ -85,7 +107,7 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
85
107
  });
86
108
  setQd({ ...qd, where: newWhere });
87
109
  setShowAllMdWhere(true);
88
- }, [qd, fromDTD]);
110
+ }, [qd, fromDTD, inputMids]);
89
111
  // Eseguire la ricerca quando shouldSearch è true e qd è definito
90
112
  useEffect(() => {
91
113
  if (shouldSearch && qd) {
@@ -200,8 +222,8 @@ const TMSearchQueryPanel = ({ fromDTD, showBackToResultButton, isExpertMode = SD
200
222
  const outputMids = qd.where
201
223
  .filter(wi => wi.mid && wi.value1 && wi.value1.length > 0)
202
224
  .map(wi => ({ mid: wi.mid, value: wi.value1 }));
203
- passToArchiveCallback(outputMids);
204
- }, [passToArchiveCallback, qd?.where]);
225
+ passToArchiveCallback(outputMids, fromDTD?.id);
226
+ }, [passToArchiveCallback, qd?.where, fromDTD?.id]);
205
227
  const handleCloseFiltersConfig = useCallback(() => setShowFiltersConfig(false), []);
206
228
  const handleChooseFilters = useCallback((tid_mids) => {
207
229
  if (!fromDTD?.metadata)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topconsultnpm/sdkui-react",
3
- "version": "6.19.0-dev1.44",
3
+ "version": "6.19.0-dev1.46",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",