@lv-x-software-house/x_view 1.2.2-dev.7 → 1.2.2-dev.9

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 (3) hide show
  1. package/dist/index.js +116 -30
  2. package/dist/index.mjs +118 -32
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1649,27 +1649,50 @@ var userActionHandlers = {
1649
1649
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1650
1650
  },
1651
1651
  handleSaveNode: async (context, newNodeData) => {
1652
- const { graphDataRef, sceneDataRef, stateRef, creationMode, setters } = context;
1652
+ const { graphDataRef, sceneDataRef, stateRef, creationMode, setters, actions } = context;
1653
1653
  if (!graphDataRef.current || !sceneDataRef.current) return;
1654
1654
  const { sourceNodeData } = creationMode;
1655
- const newNode = { id: import_short_uuid.default.generate(), ...newNodeData };
1655
+ const { targetDatasetId, ...nodeDataToSave } = newNodeData;
1656
+ const newNode = { id: import_short_uuid.default.generate(), ...nodeDataToSave };
1656
1657
  const newLink = { id: `link_${import_short_uuid.default.generate()}`, source: sourceNodeData.id, target: newNode.id };
1657
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1658
- if (!parentInfo || !parentInfo.ownerId) {
1659
- console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1660
- alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
1658
+ const sourceParentInfo = stateRef.current.nodeIdToParentFileMap.get(String(sourceNodeData.id));
1659
+ const finalTargetDatasetId = targetDatasetId || sourceParentInfo.parentFileId;
1660
+ const targetParentInfo = sceneDataRef.current.parent_dbs.find((db) => String(db.db_id) === String(finalTargetDatasetId));
1661
+ if (!sourceParentInfo || !targetParentInfo) {
1662
+ alert("Erro ao identificar os datasets de origem ou destino.");
1661
1663
  return;
1662
1664
  }
1663
- const { parentFileId, ownerId } = parentInfo;
1664
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1665
- specificParentData.nodes.push(newNode);
1666
- specificParentData.links.push(newLink);
1667
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1665
+ const isCrossDataset = String(sourceParentInfo.parentFileId) !== String(finalTargetDatasetId);
1666
+ const sourceDataToUpdate = JSON.parse(JSON.stringify(graphDataRef.current[sourceParentInfo.parentFileId]));
1667
+ let targetDataToUpdate = isCrossDataset ? JSON.parse(JSON.stringify(graphDataRef.current[finalTargetDatasetId])) : sourceDataToUpdate;
1668
+ targetDataToUpdate.nodes.push(newNode);
1669
+ sourceDataToUpdate.links.push(newLink);
1670
+ const savePromises = [];
1671
+ if (isCrossDataset) {
1672
+ savePromises.push(
1673
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1674
+ );
1675
+ savePromises.push(
1676
+ actions.save_view_data(`x_view_dbs/${targetParentInfo.owner_id}/${finalTargetDatasetId}`, targetDataToUpdate)
1677
+ );
1678
+ } else {
1679
+ savePromises.push(
1680
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1681
+ );
1682
+ }
1668
1683
  try {
1669
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1670
- graphDataRef.current[parentFileId] = specificParentData;
1684
+ await Promise.all(savePromises);
1685
+ graphDataRef.current[sourceParentInfo.parentFileId] = sourceDataToUpdate;
1686
+ if (isCrossDataset) {
1687
+ graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1688
+ }
1671
1689
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1672
1690
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1691
+ stateRef.current.nodeIdToParentFileMap.set(String(newNode.id), {
1692
+ parentFileId: finalTargetDatasetId,
1693
+ ownerId: targetParentInfo.owner_id,
1694
+ datasetName: targetDataToUpdate.dataset_name || "Dataset Desconhecido"
1695
+ });
1673
1696
  setters.setSceneVersion((v) => v + 1);
1674
1697
  } catch (error) {
1675
1698
  console.error("Falha ao salvar os dados do grafo:", error);
@@ -6779,8 +6802,12 @@ function InSceneCreationForm({
6779
6802
  availableAncestries = [],
6780
6803
  onMentionClick,
6781
6804
  sourceTypes,
6782
- onUploadFile
6805
+ onUploadFile,
6806
+ availableDatasets = [],
6807
+ sourceNodeDatasetId,
6808
+ viewType
6783
6809
  }) {
6810
+ var _a;
6784
6811
  const [name, setName] = (0, import_react13.useState)("");
6785
6812
  const [types, setTypes] = (0, import_react13.useState)([]);
6786
6813
  const [typeInput, setTypeInput] = (0, import_react13.useState)("");
@@ -6794,6 +6821,25 @@ function InSceneCreationForm({
6794
6821
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react13.useState)(false);
6795
6822
  const [useImageAsTexture, setUseImageAsTexture] = (0, import_react13.useState)(false);
6796
6823
  const [selectedImageUrl, setSelectedImageUrl] = (0, import_react13.useState)(null);
6824
+ const [targetDatasetId, setTargetDatasetId] = (0, import_react13.useState)(sourceNodeDatasetId || "");
6825
+ const [isDatasetDropdownOpen, setIsDatasetDropdownOpen] = (0, import_react13.useState)(false);
6826
+ const datasetDropdownRef = (0, import_react13.useRef)(null);
6827
+ (0, import_react13.useEffect)(() => {
6828
+ if (sourceNodeDatasetId) setTargetDatasetId(sourceNodeDatasetId);
6829
+ }, [sourceNodeDatasetId]);
6830
+ (0, import_react13.useEffect)(() => {
6831
+ function handleClickOutside(event) {
6832
+ if (datasetDropdownRef.current && !datasetDropdownRef.current.contains(event.target)) {
6833
+ setIsDatasetDropdownOpen(false);
6834
+ }
6835
+ }
6836
+ if (isDatasetDropdownOpen) {
6837
+ document.addEventListener("mousedown", handleClickOutside);
6838
+ }
6839
+ return () => {
6840
+ document.removeEventListener("mousedown", handleClickOutside);
6841
+ };
6842
+ }, [isDatasetDropdownOpen]);
6797
6843
  const propsEndRef = (0, import_react13.useRef)(null);
6798
6844
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
6799
6845
  (0, import_react13.useEffect)(() => {
@@ -6839,8 +6885,8 @@ function InSceneCreationForm({
6839
6885
  const newProp = createNewCustomProperty(customProps);
6840
6886
  setCustomProps([...customProps, newProp]);
6841
6887
  setTimeout(() => {
6842
- var _a;
6843
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
6888
+ var _a2;
6889
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
6844
6890
  }, 100);
6845
6891
  };
6846
6892
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -6873,12 +6919,12 @@ function InSceneCreationForm({
6873
6919
  onSizeChange == null ? void 0 : onSizeChange(newSize);
6874
6920
  };
6875
6921
  const handleToggleImageMode = () => {
6876
- var _a, _b;
6922
+ var _a2, _b;
6877
6923
  const newValue = !useImageAsTexture;
6878
6924
  setUseImageAsTexture(newValue);
6879
6925
  if (newValue) {
6880
6926
  const firstImageProp = customProps.find((p) => p.type === "images");
6881
- if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
6927
+ if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
6882
6928
  const url = firstImageProp.value[0].value;
6883
6929
  setSelectedImageUrl(url);
6884
6930
  onImageChange == null ? void 0 : onImageChange(true, url);
@@ -6911,6 +6957,7 @@ function InSceneCreationForm({
6911
6957
  description_sections: processedSections,
6912
6958
  useImageAsTexture,
6913
6959
  textureImageUrl: useImageAsTexture ? selectedImageUrl : null,
6960
+ targetDatasetId,
6914
6961
  ...additionalData
6915
6962
  });
6916
6963
  };
@@ -6931,6 +6978,7 @@ function InSceneCreationForm({
6931
6978
  onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
6932
6979
  }
6933
6980
  };
6981
+ const selectedDatasetName = ((_a = availableDatasets.find((ds) => ds.id === targetDatasetId)) == null ? void 0 : _a.name) || "Selecione um Dataset...";
6934
6982
  return /* @__PURE__ */ import_react13.default.createElement(import_react13.default.Fragment, null, /* @__PURE__ */ import_react13.default.createElement(
6935
6983
  "div",
6936
6984
  {
@@ -6988,7 +7036,27 @@ function InSceneCreationForm({
6988
7036
  }
6989
7037
  },
6990
7038
  suggestedType
6991
- ))))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ import_react13.default.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react13.default.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react13.default.createElement(
7039
+ ))))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ import_react13.default.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), viewType === "view" && availableDatasets.length > 0 && /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5 relative", ref: datasetDropdownRef }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Criar Node no Dataset:"), /* @__PURE__ */ import_react13.default.createElement(
7040
+ "button",
7041
+ {
7042
+ type: "button",
7043
+ onClick: () => setIsDatasetDropdownOpen(!isDatasetDropdownOpen),
7044
+ className: "w-full flex items-center justify-between bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 hover:bg-slate-700/70 transition-colors text-slate-200 text-left"
7045
+ },
7046
+ /* @__PURE__ */ import_react13.default.createElement("span", { className: "truncate pr-2" }, selectedDatasetName),
7047
+ /* @__PURE__ */ import_react13.default.createElement(import_fi12.FiChevronDown, { className: `flex-shrink-0 text-slate-400 transition-transform ${isDatasetDropdownOpen ? "rotate-180" : ""}` })
7048
+ ), isDatasetDropdownOpen && /* @__PURE__ */ import_react13.default.createElement("ul", { className: "custom-scrollbar absolute top-[66px] left-0 z-20 w-full max-h-48 overflow-y-auto rounded-lg bg-slate-800 border border-white/10 shadow-xl py-1" }, availableDatasets.map((ds) => /* @__PURE__ */ import_react13.default.createElement(
7049
+ "li",
7050
+ {
7051
+ key: ds.id,
7052
+ onClick: () => {
7053
+ setTargetDatasetId(ds.id);
7054
+ setIsDatasetDropdownOpen(false);
7055
+ },
7056
+ className: `px-3 py-2 text-sm cursor-pointer transition-colors ${targetDatasetId === ds.id ? "bg-indigo-600/40 text-indigo-200 font-medium" : "text-slate-300 hover:bg-white/5"}`
7057
+ },
7058
+ ds.name
7059
+ )))), /* @__PURE__ */ import_react13.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react13.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react13.default.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react13.default.createElement(
6992
7060
  DescriptionDisplay,
6993
7061
  {
6994
7062
  description,
@@ -7324,7 +7392,8 @@ function NodeDetailsPanel({
7324
7392
  onMentionClick,
7325
7393
  onIntensityChange,
7326
7394
  onUploadFile,
7327
- userRole
7395
+ userRole,
7396
+ currentDatasetName
7328
7397
  }) {
7329
7398
  const [name, setName] = (0, import_react15.useState)((node == null ? void 0 : node.name) ?? "");
7330
7399
  const [types, setTypes] = (0, import_react15.useState)([]);
@@ -7735,7 +7804,7 @@ function NodeDetailsPanel({
7735
7804
  onUploadFile: canEdit ? onUploadFile : void 0,
7736
7805
  readOnly: !canEdit
7737
7806
  }
7738
- )), /* @__PURE__ */ import_react15.default.createElement("div", { ref: propsEndRef })))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ import_react15.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ import_react15.default.createElement(
7807
+ )), /* @__PURE__ */ import_react15.default.createElement("div", { ref: propsEndRef }))), currentDatasetName && /* @__PURE__ */ import_react15.default.createElement("div", { className: "pt-3 mt-4 border-t border-white/10 flex items-center gap-2 text-xs text-slate-400" }, /* @__PURE__ */ import_react15.default.createElement(import_fi14.FiDatabase, { className: "text-indigo-400 shrink-0", size: 14 }), /* @__PURE__ */ import_react15.default.createElement("span", { className: "truncate" }, "Dataset ", /* @__PURE__ */ import_react15.default.createElement("span", { className: "text-slate-200 font-medium" }, currentDatasetName)))), /* @__PURE__ */ import_react15.default.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ import_react15.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ import_react15.default.createElement(
7739
7808
  "button",
7740
7809
  {
7741
7810
  onClick: () => handleSave(false),
@@ -9056,7 +9125,7 @@ function XViewScene({
9056
9125
  delete_file_action,
9057
9126
  check_user_permission
9058
9127
  }) {
9059
- var _a, _b, _c, _d, _e, _f;
9128
+ var _a, _b, _c, _d, _e, _f, _g;
9060
9129
  const { data: session, status } = (0, import_react24.useSession)();
9061
9130
  const router = (0, import_navigation.useRouter)();
9062
9131
  const searchParams = (0, import_navigation.useSearchParams)();
@@ -9208,9 +9277,10 @@ function XViewScene({
9208
9277
  const parentFile = allParentData[parentFileId];
9209
9278
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
9210
9279
  const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
9280
+ const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
9211
9281
  if (parentFile.nodes && ownerId2) {
9212
9282
  for (const node of parentFile.nodes) {
9213
- map.set(String(node.id), { parentFileId, ownerId: ownerId2 });
9283
+ map.set(String(node.id), { parentFileId, ownerId: ownerId2, datasetName });
9214
9284
  }
9215
9285
  }
9216
9286
  }
@@ -12059,6 +12129,18 @@ function XViewScene({
12059
12129
  tweenToTarget(nodeMesh, 1.2);
12060
12130
  }
12061
12131
  }, [tweenToTarget]);
12132
+ const availableDatasets = (0, import_react23.useMemo)(() => {
12133
+ if (!sceneDataRef.current || !parentDataRef.current) return [];
12134
+ return sceneDataRef.current.parent_dbs.map((db) => {
12135
+ var _a2;
12136
+ return {
12137
+ id: db.db_id,
12138
+ name: ((_a2 = parentDataRef.current[db.db_id]) == null ? void 0 : _a2.dataset_name) || `Dataset #${db.db_id.substring(0, 6)}`
12139
+ };
12140
+ });
12141
+ }, [sceneVersion, isInitialized]);
12142
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(String(creationMode.sourceNodeData.id))) == null ? void 0 : _b.parentFileId : null;
12143
+ const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
12062
12144
  (0, import_react23.useEffect)(() => {
12063
12145
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
12064
12146
  const nodeObjects = stateRef.current.nodeObjects || {};
@@ -12148,10 +12230,13 @@ function XViewScene({
12148
12230
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12149
12231
  refEl: formRef,
12150
12232
  existingTypes: existingNodeTypes,
12151
- initialColor: (_b = creationMode.sourceNodeData) == null ? void 0 : _b.color,
12152
- sourceTypes: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.type,
12233
+ initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
12234
+ sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
12153
12235
  onIntensityChange: handleGhostNodeIntensityChange,
12154
- onUploadFile: upload_file_action
12236
+ onUploadFile: upload_file_action,
12237
+ availableDatasets,
12238
+ sourceNodeDatasetId,
12239
+ viewType: viewParams == null ? void 0 : viewParams.type
12155
12240
  }
12156
12241
  ),
12157
12242
  versionMode.isActive && /* @__PURE__ */ import_react23.default.createElement(
@@ -12166,8 +12251,8 @@ function XViewScene({
12166
12251
  onMentionClick: handleAddExistingNode,
12167
12252
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12168
12253
  refEl: formRef,
12169
- fixedType: (_d = versionMode.sourceNodeData) == null ? void 0 : _d.type,
12170
- fixedColor: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.color,
12254
+ fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
12255
+ fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
12171
12256
  onUploadFile: upload_file_action
12172
12257
  }
12173
12258
  ),
@@ -12276,7 +12361,8 @@ function XViewScene({
12276
12361
  onMentionClick: handleAddExistingNode,
12277
12362
  onIntensityChange: handleDetailNodeIntensityChange,
12278
12363
  onUploadFile: upload_file_action,
12279
- userRole: userPermissionRole
12364
+ userRole: userPermissionRole,
12365
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
12280
12366
  }
12281
12367
  ),
12282
12368
  detailsLink && /* @__PURE__ */ import_react23.default.createElement(
@@ -12404,7 +12490,7 @@ function XViewScene({
12404
12490
  onClose: () => setIsImportModalOpen(false),
12405
12491
  onConfirm: handleConfirmImport,
12406
12492
  session,
12407
- parentDbs: ((_f = sceneDataRef.current) == null ? void 0 : _f.parent_dbs) || [],
12493
+ parentDbs: ((_g = sceneDataRef.current) == null ? void 0 : _g.parent_dbs) || [],
12408
12494
  onFetchAvailableFiles: import_parent_file_modal_get,
12409
12495
  currentViewName: viewParams == null ? void 0 : viewParams.name,
12410
12496
  currentAncestries: ancestryDataRef.current || []
package/dist/index.mjs CHANGED
@@ -1605,27 +1605,50 @@ var userActionHandlers = {
1605
1605
  setters.setFormPosition((p) => ({ ...p, opacity: 0 }));
1606
1606
  },
1607
1607
  handleSaveNode: async (context, newNodeData) => {
1608
- const { graphDataRef, sceneDataRef, stateRef, creationMode, setters } = context;
1608
+ const { graphDataRef, sceneDataRef, stateRef, creationMode, setters, actions } = context;
1609
1609
  if (!graphDataRef.current || !sceneDataRef.current) return;
1610
1610
  const { sourceNodeData } = creationMode;
1611
- const newNode = { id: short.generate(), ...newNodeData };
1611
+ const { targetDatasetId, ...nodeDataToSave } = newNodeData;
1612
+ const newNode = { id: short.generate(), ...nodeDataToSave };
1612
1613
  const newLink = { id: `link_${short.generate()}`, source: sourceNodeData.id, target: newNode.id };
1613
- const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, sourceNodeData.id);
1614
- if (!parentInfo || !parentInfo.ownerId) {
1615
- console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:", sourceNodeData.id);
1616
- alert("Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio.");
1614
+ const sourceParentInfo = stateRef.current.nodeIdToParentFileMap.get(String(sourceNodeData.id));
1615
+ const finalTargetDatasetId = targetDatasetId || sourceParentInfo.parentFileId;
1616
+ const targetParentInfo = sceneDataRef.current.parent_dbs.find((db) => String(db.db_id) === String(finalTargetDatasetId));
1617
+ if (!sourceParentInfo || !targetParentInfo) {
1618
+ alert("Erro ao identificar os datasets de origem ou destino.");
1617
1619
  return;
1618
1620
  }
1619
- const { parentFileId, ownerId } = parentInfo;
1620
- const specificParentData = JSON.parse(JSON.stringify(graphDataRef.current[parentFileId]));
1621
- specificParentData.nodes.push(newNode);
1622
- specificParentData.links.push(newLink);
1623
- const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
1621
+ const isCrossDataset = String(sourceParentInfo.parentFileId) !== String(finalTargetDatasetId);
1622
+ const sourceDataToUpdate = JSON.parse(JSON.stringify(graphDataRef.current[sourceParentInfo.parentFileId]));
1623
+ let targetDataToUpdate = isCrossDataset ? JSON.parse(JSON.stringify(graphDataRef.current[finalTargetDatasetId])) : sourceDataToUpdate;
1624
+ targetDataToUpdate.nodes.push(newNode);
1625
+ sourceDataToUpdate.links.push(newLink);
1626
+ const savePromises = [];
1627
+ if (isCrossDataset) {
1628
+ savePromises.push(
1629
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1630
+ );
1631
+ savePromises.push(
1632
+ actions.save_view_data(`x_view_dbs/${targetParentInfo.owner_id}/${finalTargetDatasetId}`, targetDataToUpdate)
1633
+ );
1634
+ } else {
1635
+ savePromises.push(
1636
+ actions.save_view_data(`x_view_dbs/${sourceParentInfo.ownerId}/${sourceParentInfo.parentFileId}`, sourceDataToUpdate)
1637
+ );
1638
+ }
1624
1639
  try {
1625
- await context.actions.save_view_data(filenameForSpecificParent, specificParentData);
1626
- graphDataRef.current[parentFileId] = specificParentData;
1640
+ await Promise.all(savePromises);
1641
+ graphDataRef.current[sourceParentInfo.parentFileId] = sourceDataToUpdate;
1642
+ if (isCrossDataset) {
1643
+ graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1644
+ }
1627
1645
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1628
1646
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
1647
+ stateRef.current.nodeIdToParentFileMap.set(String(newNode.id), {
1648
+ parentFileId: finalTargetDatasetId,
1649
+ ownerId: targetParentInfo.owner_id,
1650
+ datasetName: targetDataToUpdate.dataset_name || "Dataset Desconhecido"
1651
+ });
1629
1652
  setters.setSceneVersion((v) => v + 1);
1630
1653
  } catch (error) {
1631
1654
  console.error("Falha ao salvar os dados do grafo:", error);
@@ -6747,7 +6770,7 @@ function ColorPicker({ color, onChange, disabled }) {
6747
6770
  }
6748
6771
 
6749
6772
  // src/components/InSceneCreationForm.jsx
6750
- import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun } from "react-icons/fi";
6773
+ import { FiPlus as FiPlus3, FiMaximize2, FiX as FiX3, FiCheck as FiCheck7, FiEdit2 as FiEdit24, FiSun, FiChevronDown as FiChevronDown4 } from "react-icons/fi";
6751
6774
  function InSceneCreationForm({
6752
6775
  onSave,
6753
6776
  onCancel,
@@ -6765,8 +6788,12 @@ function InSceneCreationForm({
6765
6788
  availableAncestries = [],
6766
6789
  onMentionClick,
6767
6790
  sourceTypes,
6768
- onUploadFile
6791
+ onUploadFile,
6792
+ availableDatasets = [],
6793
+ sourceNodeDatasetId,
6794
+ viewType
6769
6795
  }) {
6796
+ var _a;
6770
6797
  const [name, setName] = useState13("");
6771
6798
  const [types, setTypes] = useState13([]);
6772
6799
  const [typeInput, setTypeInput] = useState13("");
@@ -6780,6 +6807,25 @@ function InSceneCreationForm({
6780
6807
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState13(false);
6781
6808
  const [useImageAsTexture, setUseImageAsTexture] = useState13(false);
6782
6809
  const [selectedImageUrl, setSelectedImageUrl] = useState13(null);
6810
+ const [targetDatasetId, setTargetDatasetId] = useState13(sourceNodeDatasetId || "");
6811
+ const [isDatasetDropdownOpen, setIsDatasetDropdownOpen] = useState13(false);
6812
+ const datasetDropdownRef = useRef10(null);
6813
+ useEffect13(() => {
6814
+ if (sourceNodeDatasetId) setTargetDatasetId(sourceNodeDatasetId);
6815
+ }, [sourceNodeDatasetId]);
6816
+ useEffect13(() => {
6817
+ function handleClickOutside(event) {
6818
+ if (datasetDropdownRef.current && !datasetDropdownRef.current.contains(event.target)) {
6819
+ setIsDatasetDropdownOpen(false);
6820
+ }
6821
+ }
6822
+ if (isDatasetDropdownOpen) {
6823
+ document.addEventListener("mousedown", handleClickOutside);
6824
+ }
6825
+ return () => {
6826
+ document.removeEventListener("mousedown", handleClickOutside);
6827
+ };
6828
+ }, [isDatasetDropdownOpen]);
6783
6829
  const propsEndRef = useRef10(null);
6784
6830
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
6785
6831
  useEffect13(() => {
@@ -6825,8 +6871,8 @@ function InSceneCreationForm({
6825
6871
  const newProp = createNewCustomProperty(customProps);
6826
6872
  setCustomProps([...customProps, newProp]);
6827
6873
  setTimeout(() => {
6828
- var _a;
6829
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
6874
+ var _a2;
6875
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
6830
6876
  }, 100);
6831
6877
  };
6832
6878
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -6859,12 +6905,12 @@ function InSceneCreationForm({
6859
6905
  onSizeChange == null ? void 0 : onSizeChange(newSize);
6860
6906
  };
6861
6907
  const handleToggleImageMode = () => {
6862
- var _a, _b;
6908
+ var _a2, _b;
6863
6909
  const newValue = !useImageAsTexture;
6864
6910
  setUseImageAsTexture(newValue);
6865
6911
  if (newValue) {
6866
6912
  const firstImageProp = customProps.find((p) => p.type === "images");
6867
- if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
6913
+ if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
6868
6914
  const url = firstImageProp.value[0].value;
6869
6915
  setSelectedImageUrl(url);
6870
6916
  onImageChange == null ? void 0 : onImageChange(true, url);
@@ -6897,6 +6943,7 @@ function InSceneCreationForm({
6897
6943
  description_sections: processedSections,
6898
6944
  useImageAsTexture,
6899
6945
  textureImageUrl: useImageAsTexture ? selectedImageUrl : null,
6946
+ targetDatasetId,
6900
6947
  ...additionalData
6901
6948
  });
6902
6949
  };
@@ -6917,6 +6964,7 @@ function InSceneCreationForm({
6917
6964
  onOpenImageViewer([{ name: name2 || "Imagem", value: url }], 0);
6918
6965
  }
6919
6966
  };
6967
+ const selectedDatasetName = ((_a = availableDatasets.find((ds) => ds.id === targetDatasetId)) == null ? void 0 : _a.name) || "Selecione um Dataset...";
6920
6968
  return /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
6921
6969
  "div",
6922
6970
  {
@@ -6974,7 +7022,27 @@ function InSceneCreationForm({
6974
7022
  }
6975
7023
  },
6976
7024
  suggestedType
6977
- ))))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ React13.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ React13.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React13.createElement(
7025
+ ))))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Nome do Node"), /* @__PURE__ */ React13.createElement("input", { required: true, type: "text", placeholder: "Ex.: Cliente XPTO", value: name, onChange: handleNameInputChange, className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60" })), viewType === "view" && availableDatasets.length > 0 && /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5 relative", ref: datasetDropdownRef }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Criar Node no Dataset:"), /* @__PURE__ */ React13.createElement(
7026
+ "button",
7027
+ {
7028
+ type: "button",
7029
+ onClick: () => setIsDatasetDropdownOpen(!isDatasetDropdownOpen),
7030
+ className: "w-full flex items-center justify-between bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 hover:bg-slate-700/70 transition-colors text-slate-200 text-left"
7031
+ },
7032
+ /* @__PURE__ */ React13.createElement("span", { className: "truncate pr-2" }, selectedDatasetName),
7033
+ /* @__PURE__ */ React13.createElement(FiChevronDown4, { className: `flex-shrink-0 text-slate-400 transition-transform ${isDatasetDropdownOpen ? "rotate-180" : ""}` })
7034
+ ), isDatasetDropdownOpen && /* @__PURE__ */ React13.createElement("ul", { className: "custom-scrollbar absolute top-[66px] left-0 z-20 w-full max-h-48 overflow-y-auto rounded-lg bg-slate-800 border border-white/10 shadow-xl py-1" }, availableDatasets.map((ds) => /* @__PURE__ */ React13.createElement(
7035
+ "li",
7036
+ {
7037
+ key: ds.id,
7038
+ onClick: () => {
7039
+ setTargetDatasetId(ds.id);
7040
+ setIsDatasetDropdownOpen(false);
7041
+ },
7042
+ className: `px-3 py-2 text-sm cursor-pointer transition-colors ${targetDatasetId === ds.id ? "bg-indigo-600/40 text-indigo-200 font-medium" : "text-slate-300 hover:bg-white/5"}`
7043
+ },
7044
+ ds.name
7045
+ )))), /* @__PURE__ */ React13.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React13.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o (Opcional)"), /* @__PURE__ */ React13.createElement("div", { className: "relative group min-h-[80px] bg-slate-800/70 p-2.5 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React13.createElement(
6978
7046
  DescriptionDisplay,
6979
7047
  {
6980
7048
  description,
@@ -7292,7 +7360,7 @@ function InSceneVersionForm({
7292
7360
 
7293
7361
  // src/components/NodeDetailsPanel.jsx
7294
7362
  import React15, { useState as useState15, useEffect as useEffect15, useRef as useRef12 } from "react";
7295
- import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize23, FiX as FiX4, FiCheck as FiCheck9, FiImage as FiImage3, FiEdit2 as FiEdit26, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5 } from "react-icons/fi";
7363
+ import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize23, FiX as FiX4, FiCheck as FiCheck9, FiImage as FiImage3, FiEdit2 as FiEdit26, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5, FiDatabase } from "react-icons/fi";
7296
7364
  function NodeDetailsPanel({
7297
7365
  node,
7298
7366
  onClose,
@@ -7310,7 +7378,8 @@ function NodeDetailsPanel({
7310
7378
  onMentionClick,
7311
7379
  onIntensityChange,
7312
7380
  onUploadFile,
7313
- userRole
7381
+ userRole,
7382
+ currentDatasetName
7314
7383
  }) {
7315
7384
  const [name, setName] = useState15((node == null ? void 0 : node.name) ?? "");
7316
7385
  const [types, setTypes] = useState15([]);
@@ -7721,7 +7790,7 @@ function NodeDetailsPanel({
7721
7790
  onUploadFile: canEdit ? onUploadFile : void 0,
7722
7791
  readOnly: !canEdit
7723
7792
  }
7724
- )), /* @__PURE__ */ React15.createElement("div", { ref: propsEndRef })))), /* @__PURE__ */ React15.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ React15.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ React15.createElement(
7793
+ )), /* @__PURE__ */ React15.createElement("div", { ref: propsEndRef }))), currentDatasetName && /* @__PURE__ */ React15.createElement("div", { className: "pt-3 mt-4 border-t border-white/10 flex items-center gap-2 text-xs text-slate-400" }, /* @__PURE__ */ React15.createElement(FiDatabase, { className: "text-indigo-400 shrink-0", size: 14 }), /* @__PURE__ */ React15.createElement("span", { className: "truncate" }, "Dataset ", /* @__PURE__ */ React15.createElement("span", { className: "text-slate-200 font-medium" }, currentDatasetName)))), /* @__PURE__ */ React15.createElement("div", { className: "sticky bottom-0 z-10 bg-gradient-to-t from-slate-950/80 via-slate-950/50 to-transparent px-6 py-4 border-t border-white/10 flex justify-end gap-3" }, /* @__PURE__ */ React15.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "px-4 py-2 rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-sm disabled:opacity-50" }, canEdit ? "Cancelar" : "Fechar"), canEdit && /* @__PURE__ */ React15.createElement(
7725
7794
  "button",
7726
7795
  {
7727
7796
  onClick: () => handleSave(false),
@@ -9055,7 +9124,7 @@ function XViewScene({
9055
9124
  delete_file_action,
9056
9125
  check_user_permission
9057
9126
  }) {
9058
- var _a, _b, _c, _d, _e, _f;
9127
+ var _a, _b, _c, _d, _e, _f, _g;
9059
9128
  const { data: session, status } = useSession();
9060
9129
  const router = useRouter();
9061
9130
  const searchParams = useSearchParams();
@@ -9207,9 +9276,10 @@ function XViewScene({
9207
9276
  const parentFile = allParentData[parentFileId];
9208
9277
  const parentDbInfo = parentDbsArray.find((db) => String(db.db_id) === String(parentFileId));
9209
9278
  const ownerId2 = (parentDbInfo == null ? void 0 : parentDbInfo.owner_id) || null;
9279
+ const datasetName = parentFile.dataset_name || `Dataset #${parentFileId.substring(0, 6)}`;
9210
9280
  if (parentFile.nodes && ownerId2) {
9211
9281
  for (const node of parentFile.nodes) {
9212
- map.set(String(node.id), { parentFileId, ownerId: ownerId2 });
9282
+ map.set(String(node.id), { parentFileId, ownerId: ownerId2, datasetName });
9213
9283
  }
9214
9284
  }
9215
9285
  }
@@ -12058,6 +12128,18 @@ function XViewScene({
12058
12128
  tweenToTarget(nodeMesh, 1.2);
12059
12129
  }
12060
12130
  }, [tweenToTarget]);
12131
+ const availableDatasets = useMemo12(() => {
12132
+ if (!sceneDataRef.current || !parentDataRef.current) return [];
12133
+ return sceneDataRef.current.parent_dbs.map((db) => {
12134
+ var _a2;
12135
+ return {
12136
+ id: db.db_id,
12137
+ name: ((_a2 = parentDataRef.current[db.db_id]) == null ? void 0 : _a2.dataset_name) || `Dataset #${db.db_id.substring(0, 6)}`
12138
+ };
12139
+ });
12140
+ }, [sceneVersion, isInitialized]);
12141
+ const sourceNodeDatasetId = creationMode.sourceNodeData ? (_b = stateRef.current.nodeIdToParentFileMap.get(String(creationMode.sourceNodeData.id))) == null ? void 0 : _b.parentFileId : null;
12142
+ const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
12061
12143
  useEffect21(() => {
12062
12144
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
12063
12145
  const nodeObjects = stateRef.current.nodeObjects || {};
@@ -12147,10 +12229,13 @@ function XViewScene({
12147
12229
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12148
12230
  refEl: formRef,
12149
12231
  existingTypes: existingNodeTypes,
12150
- initialColor: (_b = creationMode.sourceNodeData) == null ? void 0 : _b.color,
12151
- sourceTypes: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.type,
12232
+ initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
12233
+ sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
12152
12234
  onIntensityChange: handleGhostNodeIntensityChange,
12153
- onUploadFile: upload_file_action
12235
+ onUploadFile: upload_file_action,
12236
+ availableDatasets,
12237
+ sourceNodeDatasetId,
12238
+ viewType: viewParams == null ? void 0 : viewParams.type
12154
12239
  }
12155
12240
  ),
12156
12241
  versionMode.isActive && /* @__PURE__ */ React23.createElement(
@@ -12165,8 +12250,8 @@ function XViewScene({
12165
12250
  onMentionClick: handleAddExistingNode,
12166
12251
  style: { position: "absolute", left: `${formPosition.left}px`, top: `${formPosition.top}px`, opacity: formPosition.opacity, zIndex: 20, transition: "opacity 200ms ease-out" },
12167
12252
  refEl: formRef,
12168
- fixedType: (_d = versionMode.sourceNodeData) == null ? void 0 : _d.type,
12169
- fixedColor: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.color,
12253
+ fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
12254
+ fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
12170
12255
  onUploadFile: upload_file_action
12171
12256
  }
12172
12257
  ),
@@ -12275,7 +12360,8 @@ function XViewScene({
12275
12360
  onMentionClick: handleAddExistingNode,
12276
12361
  onIntensityChange: handleDetailNodeIntensityChange,
12277
12362
  onUploadFile: upload_file_action,
12278
- userRole: userPermissionRole
12363
+ userRole: userPermissionRole,
12364
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
12279
12365
  }
12280
12366
  ),
12281
12367
  detailsLink && /* @__PURE__ */ React23.createElement(
@@ -12403,7 +12489,7 @@ function XViewScene({
12403
12489
  onClose: () => setIsImportModalOpen(false),
12404
12490
  onConfirm: handleConfirmImport,
12405
12491
  session,
12406
- parentDbs: ((_f = sceneDataRef.current) == null ? void 0 : _f.parent_dbs) || [],
12492
+ parentDbs: ((_g = sceneDataRef.current) == null ? void 0 : _g.parent_dbs) || [],
12407
12493
  onFetchAvailableFiles: import_parent_file_modal_get,
12408
12494
  currentViewName: viewParams == null ? void 0 : viewParams.name,
12409
12495
  currentAncestries: ancestryDataRef.current || []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.2-dev.7",
3
+ "version": "1.2.2-dev.9",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",