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

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 +451 -417
  2. package/dist/index.mjs +489 -455
  3. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@
2
2
  import React32, {
3
3
  useCallback as useCallback7,
4
4
  useEffect as useEffect25,
5
- useRef as useRef21,
5
+ useRef as useRef22,
6
6
  useState as useState30,
7
7
  useMemo as useMemo14
8
8
  } from "react";
@@ -283,7 +283,12 @@ var updateExistingNodeVisuals = (state, updatedNode) => {
283
283
  if (oldLabelObject.material) oldLabelObject.material.dispose();
284
284
  }
285
285
  }
286
- const newMesh = createNodeMesh(updatedNode, position, state.glowTexture, hadQuestDateLabel);
286
+ const newMesh = createNodeMesh(
287
+ updatedNode,
288
+ position,
289
+ state.glowTexture,
290
+ hadQuestDateLabel
291
+ );
287
292
  if (oldTimelineBar) {
288
293
  newMesh.userData.timelineIntervalBar = oldTimelineBar;
289
294
  }
@@ -835,7 +840,12 @@ var removeNodeFromScene = (state, nodeId) => {
835
840
  var addStandaloneNodeToScene = (state, nodeData, position, forceShowQuestDate = false) => {
836
841
  const { graphGroup, nodeObjects, clickableNodes, glowTexture } = state;
837
842
  if (!graphGroup) return;
838
- const mesh = createNodeMesh(nodeData, position, glowTexture, forceShowQuestDate);
843
+ const mesh = createNodeMesh(
844
+ nodeData,
845
+ position,
846
+ glowTexture,
847
+ forceShowQuestDate
848
+ );
839
849
  mesh.scale.set(0.01, 0.01, 0.01);
840
850
  graphGroup.add(mesh);
841
851
  if (mesh.userData.labelObject) {
@@ -1053,7 +1063,9 @@ var userActionHandlers = {
1053
1063
  if (isCrossDataset) {
1054
1064
  graphDataRef.current[finalTargetDatasetId] = targetDataToUpdate;
1055
1065
  }
1056
- if (sceneDataRef.current.parent_dbs.some((db) => String(db.db_id) === String(sourceParentInfo.parentFileId))) {
1066
+ if (sceneDataRef.current.parent_dbs.some(
1067
+ (db) => String(db.db_id) === String(sourceParentInfo.parentFileId)
1068
+ )) {
1057
1069
  }
1058
1070
  const finalPosition = stateRef.current.ghostElements.node.position.clone();
1059
1071
  addNewNodeToScene(stateRef.current, newNode, newLink, finalPosition);
@@ -1248,7 +1260,10 @@ var userActionHandlers = {
1248
1260
  context.sceneSaveUrl,
1249
1261
  viewFilePayload
1250
1262
  );
1251
- const questSaveUrl = context.sceneSaveUrl.replace("x_view_scenes", "x_view_quests");
1263
+ const questSaveUrl = context.sceneSaveUrl.replace(
1264
+ "x_view_scenes",
1265
+ "x_view_quests"
1266
+ );
1252
1267
  await context.actions.save_view_data(questSaveUrl, questFilePayload);
1253
1268
  } else {
1254
1269
  const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
@@ -1324,7 +1339,12 @@ var userActionHandlers = {
1324
1339
  )
1325
1340
  );
1326
1341
  targetFinalPositions.push(endPosition);
1327
- const targetMesh = createNodeMesh(nodeData, startPosition, glowTexture);
1342
+ const targetMesh = createNodeMesh(
1343
+ nodeData,
1344
+ startPosition,
1345
+ glowTexture,
1346
+ !!nodeData.is_quest
1347
+ );
1328
1348
  targetMesh.scale.set(0.01, 0.01, 0.01);
1329
1349
  graphGroup.add(targetMesh);
1330
1350
  if (targetMesh.userData.labelObject) {
@@ -1476,10 +1496,16 @@ var userActionHandlers = {
1476
1496
  context.sceneConfigId,
1477
1497
  context.ownerId
1478
1498
  );
1479
- if (!sourceParentInfo || !sourceParentInfo.ownerId) {
1499
+ const targetParentInfo = getParentFileInfoForNode(
1500
+ graphDataRef.current,
1501
+ sceneDataRef.current,
1502
+ targetNodeData.id,
1503
+ context.sceneConfigId,
1504
+ context.ownerId
1505
+ );
1506
+ if (!sourceParentInfo || !sourceParentInfo.ownerId || !targetParentInfo) {
1480
1507
  console.error(
1481
- "N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node de origem:",
1482
- sourceNodeData.id
1508
+ "N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para os Nodes da conex\xE3o."
1483
1509
  );
1484
1510
  alert(
1485
1511
  "Ocorreu um erro ao identificar o arquivo pai ou seu propriet\xE1rio."
@@ -1487,9 +1513,13 @@ var userActionHandlers = {
1487
1513
  userActionHandlers.handleCancelConnection(context);
1488
1514
  return;
1489
1515
  }
1490
- const { parentFileId: sourceParentFileId, ownerId: sourceOwnerId } = sourceParentInfo;
1491
- const parentFileIdToSave = sourceParentFileId;
1492
- const ownerIdToSave = sourceOwnerId;
1516
+ let parentInfoToSave = sourceParentInfo;
1517
+ const isSourceQuest = sourceParentInfo.parentFileId === context.sceneConfigId;
1518
+ const isTargetQuest = targetParentInfo.parentFileId === context.sceneConfigId;
1519
+ if (isSourceQuest || isTargetQuest) {
1520
+ parentInfoToSave = { parentFileId: context.sceneConfigId, ownerId: context.ownerId };
1521
+ }
1522
+ const { parentFileId: parentFileIdToSave, ownerId: ownerIdToSave } = parentInfoToSave;
1493
1523
  const newLink = {
1494
1524
  id: `link_${short.generate()}`,
1495
1525
  source: sourceNodeData.id,
@@ -1503,14 +1533,19 @@ var userActionHandlers = {
1503
1533
  const viewFilePayload = {
1504
1534
  parent_dbs: sceneDataRef.current.parent_dbs,
1505
1535
  nodes: sceneDataRef.current.nodes,
1506
- links: sceneDataRef.current.links,
1507
- quest_counter: sceneDataRef.current.quest_counter || 1
1536
+ links: sceneDataRef.current.links
1508
1537
  };
1509
1538
  await context.actions.save_view_data(
1510
1539
  context.sceneSaveUrl,
1511
1540
  viewFilePayload
1512
1541
  );
1513
- await context.actions.save_quest_link(context.ownerId, context.sceneConfigId, newLink);
1542
+ if (context.actions.upsert_quest_link_firestore) {
1543
+ await context.actions.upsert_quest_link_firestore(
1544
+ context.ownerId,
1545
+ context.sceneConfigId,
1546
+ newLink
1547
+ );
1548
+ }
1514
1549
  } else {
1515
1550
  const specificParentData = JSON.parse(
1516
1551
  JSON.stringify(graphDataRef.current[parentFileIdToSave])
@@ -1659,18 +1694,12 @@ var userActionHandlers = {
1659
1694
  const viewFilePayload = {
1660
1695
  parent_dbs: sceneDataRef.current.parent_dbs,
1661
1696
  nodes: sceneDataRef.current.nodes,
1662
- links: sceneDataRef.current.links,
1663
- quest_counter: sceneDataRef.current.quest_counter || 1
1697
+ links: sceneDataRef.current.links
1664
1698
  };
1665
- const questFilePayload = {
1666
- nodes: data.nodes,
1667
- links: data.links,
1668
- milestones: sceneDataRef.current.milestones || []
1669
- };
1670
- const p1 = context.actions.save_view_data(context.sceneSaveUrl, viewFilePayload);
1671
- const questSaveUrl = context.sceneSaveUrl.replace("x_view_scenes", "x_view_quests");
1672
- const p2 = context.actions.save_view_data(questSaveUrl, questFilePayload);
1673
- return Promise.all([p1, p2]);
1699
+ return context.actions.save_view_data(
1700
+ context.sceneSaveUrl,
1701
+ viewFilePayload
1702
+ );
1674
1703
  } else {
1675
1704
  return context.actions.save_view_data(
1676
1705
  `x_view_dbs/${fileOwnerId}/${fileId}`,
@@ -1695,6 +1724,15 @@ var userActionHandlers = {
1695
1724
  updatedOriginalParentData
1696
1725
  )
1697
1726
  );
1727
+ if (oldGoverningFileId === context.sceneConfigId && context.actions.delete_quest_link_firestore) {
1728
+ savePromises.push(
1729
+ context.actions.delete_quest_link_firestore(
1730
+ context.ownerId,
1731
+ context.sceneConfigId,
1732
+ linkId
1733
+ )
1734
+ );
1735
+ }
1698
1736
  const updatedNewParentData = JSON.parse(
1699
1737
  JSON.stringify(graphDataRef.current[newGoverningFileId])
1700
1738
  );
@@ -1708,6 +1746,15 @@ var userActionHandlers = {
1708
1746
  updatedNewParentData
1709
1747
  )
1710
1748
  );
1749
+ if (newGoverningFileId === context.sceneConfigId && context.actions.upsert_quest_link_firestore) {
1750
+ savePromises.push(
1751
+ context.actions.upsert_quest_link_firestore(
1752
+ context.ownerId,
1753
+ context.sceneConfigId,
1754
+ newLinkData
1755
+ )
1756
+ );
1757
+ }
1711
1758
  } else {
1712
1759
  const updatedParentData = JSON.parse(
1713
1760
  JSON.stringify(graphDataRef.current[oldGoverningFileId])
@@ -1724,6 +1771,15 @@ var userActionHandlers = {
1724
1771
  updatedParentData
1725
1772
  )
1726
1773
  );
1774
+ if (oldGoverningFileId === context.sceneConfigId && context.actions.upsert_quest_link_firestore) {
1775
+ savePromises.push(
1776
+ context.actions.upsert_quest_link_firestore(
1777
+ context.ownerId,
1778
+ context.sceneConfigId,
1779
+ newLinkData
1780
+ )
1781
+ );
1782
+ }
1727
1783
  }
1728
1784
  try {
1729
1785
  await Promise.all(savePromises);
@@ -1754,14 +1810,21 @@ var userActionHandlers = {
1754
1810
  );
1755
1811
  return;
1756
1812
  }
1757
- const parentInfo = getParentFileInfoForNode(
1813
+ const sourceParentInfo = getParentFileInfoForNode(
1758
1814
  graphDataRef.current,
1759
1815
  sceneDataRef.current,
1760
1816
  linkObject.userData.source,
1761
1817
  context.sceneConfigId,
1762
1818
  context.ownerId
1763
1819
  );
1764
- if (!parentInfo || !parentInfo.ownerId) {
1820
+ const targetParentInfo = getParentFileInfoForNode(
1821
+ graphDataRef.current,
1822
+ sceneDataRef.current,
1823
+ linkObject.userData.target,
1824
+ context.sceneConfigId,
1825
+ context.ownerId
1826
+ );
1827
+ if (!sourceParentInfo || !sourceParentInfo.ownerId) {
1765
1828
  console.error(
1766
1829
  "N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o link:",
1767
1830
  linkIdToDelete
@@ -1771,6 +1834,12 @@ var userActionHandlers = {
1771
1834
  );
1772
1835
  return;
1773
1836
  }
1837
+ let parentInfo = sourceParentInfo;
1838
+ const isSourceQuest = sourceParentInfo.parentFileId === context.sceneConfigId;
1839
+ const isTargetQuest = (targetParentInfo == null ? void 0 : targetParentInfo.parentFileId) === context.sceneConfigId;
1840
+ if (isSourceQuest || isTargetQuest) {
1841
+ parentInfo = { parentFileId: context.sceneConfigId, ownerId: context.ownerId };
1842
+ }
1774
1843
  const { parentFileId, ownerId } = parentInfo;
1775
1844
  const srcName = ((_c = (_b = (_a = linkObject.userData) == null ? void 0 : _a.sourceNode) == null ? void 0 : _b.userData) == null ? void 0 : _c.name) ?? linkObject.userData.source;
1776
1845
  const tgtName = ((_f = (_e = (_d = linkObject.userData) == null ? void 0 : _d.targetNode) == null ? void 0 : _e.userData) == null ? void 0 : _f.name) ?? linkObject.userData.target;
@@ -1783,8 +1852,6 @@ var userActionHandlers = {
1783
1852
  specificParentData.links = newLinks;
1784
1853
  let filenameToSave;
1785
1854
  let payloadToSave;
1786
- let questSaveUrl;
1787
- let questFilePayload;
1788
1855
  const isView = ((_g = context.viewType) == null ? void 0 : _g.toLowerCase()) === "view";
1789
1856
  if (isView && parentFileId === context.sceneConfigId) {
1790
1857
  filenameToSave = context.sceneSaveUrl;
@@ -1794,8 +1861,7 @@ var userActionHandlers = {
1794
1861
  payloadToSave = {
1795
1862
  parent_dbs: sceneDataRef.current.parent_dbs,
1796
1863
  nodes: sceneDataRef.current.nodes,
1797
- links: sceneDataRef.current.links,
1798
- quest_counter: sceneDataRef.current.quest_counter || 1
1864
+ links: sceneDataRef.current.links
1799
1865
  };
1800
1866
  } else {
1801
1867
  filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
@@ -1803,8 +1869,12 @@ var userActionHandlers = {
1803
1869
  }
1804
1870
  try {
1805
1871
  await context.actions.save_view_data(filenameToSave, payloadToSave);
1806
- if (isView && parentFileId === context.sceneConfigId && context.actions.delete_quest_link) {
1807
- await context.actions.delete_quest_link(ownerId, context.sceneConfigId, linkIdToDelete);
1872
+ if (isView && parentFileId === context.sceneConfigId && context.actions.delete_quest_link_firestore) {
1873
+ await context.actions.delete_quest_link_firestore(
1874
+ context.ownerId,
1875
+ context.sceneConfigId,
1876
+ linkIdToDelete
1877
+ );
1808
1878
  }
1809
1879
  graphDataRef.current[parentFileId] = specificParentData;
1810
1880
  setters.setDetailsLink(
@@ -2033,18 +2103,30 @@ var userActionHandlers = {
2033
2103
  payloadToSave = {
2034
2104
  parent_dbs: sceneDataRef.current.parent_dbs,
2035
2105
  nodes: sceneDataRef.current.nodes,
2036
- links: sceneDataRef.current.links,
2037
- quest_counter: sceneDataRef.current.quest_counter || 1
2106
+ links: sceneDataRef.current.links
2038
2107
  };
2039
- for (const nodeId of nodesToDelete) {
2040
- const questNode = (originalData.nodes || []).find((n) => String(n.id) === String(nodeId));
2041
- if (questNode && questNode.is_quest) {
2042
- await context.actions.delete_quest(ownerId, context.sceneConfigId, nodeId);
2108
+ if (context.actions.delete_quest_firestore) {
2109
+ nodesToDelete.forEach((nodeIdToDelete) => {
2110
+ savePromises.push(
2111
+ context.actions.delete_quest_firestore(
2112
+ context.ownerId,
2113
+ context.sceneConfigId,
2114
+ nodeIdToDelete
2115
+ )
2116
+ );
2117
+ });
2118
+ if (context.actions.delete_quest_link_firestore) {
2119
+ linksToDelete.forEach((linkIdToDelete) => {
2120
+ savePromises.push(
2121
+ context.actions.delete_quest_link_firestore(
2122
+ context.ownerId,
2123
+ context.sceneConfigId,
2124
+ linkIdToDelete
2125
+ )
2126
+ );
2127
+ });
2043
2128
  }
2044
2129
  }
2045
- for (const linkId of linksToDelete) {
2046
- await context.actions.delete_quest_link(ownerId, context.sceneConfigId, linkId);
2047
- }
2048
2130
  } else {
2049
2131
  filenameToSave = `x_view_dbs/${ownerId}/${parentFileId}`;
2050
2132
  payloadToSave = originalData;
@@ -2115,6 +2197,9 @@ var userActionHandlers = {
2115
2197
  const newLinks = (specificParentData.links || []).filter(
2116
2198
  (l) => String(l.source) !== strNodeId && String(l.target) !== strNodeId
2117
2199
  );
2200
+ const linksToDelete = (specificParentData.links || []).filter(
2201
+ (l) => String(l.source) === strNodeId || String(l.target) === strNodeId
2202
+ );
2118
2203
  specificParentData.nodes = newNodes;
2119
2204
  specificParentData.links = newLinks;
2120
2205
  let filenameToSave;
@@ -2133,12 +2218,27 @@ var userActionHandlers = {
2133
2218
  payloadToSave = {
2134
2219
  parent_dbs: sceneDataRef.current.parent_dbs,
2135
2220
  nodes: newVisualNodes,
2136
- links: newVisualLinks,
2137
- quest_counter: sceneDataRef.current.quest_counter || 1
2221
+ links: newVisualLinks
2138
2222
  };
2139
2223
  try {
2140
2224
  await context.actions.save_view_data(filenameToSave, payloadToSave);
2141
- await context.actions.delete_quest(ownerId, context.sceneConfigId, nodeIdToDelete);
2225
+ if (context.actions.delete_quest_firestore) {
2226
+ await context.actions.delete_quest_firestore(
2227
+ context.ownerId,
2228
+ context.sceneConfigId,
2229
+ nodeIdToDelete
2230
+ );
2231
+ if (context.actions.delete_quest_link_firestore && linksToDelete.length > 0) {
2232
+ const linkDeletePromises = linksToDelete.map(
2233
+ (link) => context.actions.delete_quest_link_firestore(
2234
+ context.ownerId,
2235
+ context.sceneConfigId,
2236
+ link.id
2237
+ )
2238
+ );
2239
+ await Promise.all(linkDeletePromises);
2240
+ }
2241
+ }
2142
2242
  graphDataRef.current[parentFileId] = specificParentData;
2143
2243
  setters.setDetailsNode(
2144
2244
  (prev) => String(prev == null ? void 0 : prev.id) === String(nodeIdToDelete) ? null : prev
@@ -2168,9 +2268,10 @@ var userActionHandlers = {
2168
2268
  }
2169
2269
  },
2170
2270
  handleSaveNodeDetails: async (context, updatedNode, keepOpen = false) => {
2171
- var _a, _b, _c;
2271
+ var _a, _b, _c, _d;
2172
2272
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2173
2273
  if (!graphDataRef.current || !sceneDataRef.current) return;
2274
+ const isView = ((_a = context.viewType) == null ? void 0 : _a.toLowerCase()) === "view";
2174
2275
  const {
2175
2276
  _baseEmissiveIntensity,
2176
2277
  _baseScale,
@@ -2215,7 +2316,7 @@ var userActionHandlers = {
2215
2316
  const oldNode = specificParentData.nodes[nodeIndex];
2216
2317
  const isNewDone = oldNode.status !== "Done" && nodeToSave.status === "Done";
2217
2318
  specificParentData.nodes[nodeIndex] = { ...nodeToSave };
2218
- if (isNewDone && nodeToSave.is_quest && ((_a = nodeToSave.recurrence_config) == null ? void 0 : _a.enabled)) {
2319
+ if (isNewDone && nodeToSave.is_quest && ((_b = nodeToSave.recurrence_config) == null ? void 0 : _b.enabled)) {
2219
2320
  const nextDateObj = calculateNextRecurrenceDate(
2220
2321
  nodeToSave.recurrence_config
2221
2322
  );
@@ -2240,12 +2341,10 @@ var userActionHandlers = {
2240
2341
  }
2241
2342
  const nextQuestNode = {
2242
2343
  ...JSON.parse(JSON.stringify(nodeToSave)),
2243
- // Deep copy properties
2244
2344
  id: short.generate(),
2245
2345
  name: nextName,
2246
2346
  status: "Backlog",
2247
2347
  color: "#64748b",
2248
- // QUEST_STATUS_COLORS["Backlog"]
2249
2348
  assignee_id: nodeToSave.recurrence_config.next_assignee_id || "",
2250
2349
  Date: nextDateObj,
2251
2350
  timeline: [
@@ -2262,7 +2361,7 @@ var userActionHandlers = {
2262
2361
  ],
2263
2362
  version_node: {
2264
2363
  is_version: true,
2265
- parent_node: ((_b = nodeToSave.version_node) == null ? void 0 : _b.parent_node) || nodeToSave.id
2364
+ parent_node: ((_c = nodeToSave.version_node) == null ? void 0 : _c.parent_node) || nodeToSave.id
2266
2365
  },
2267
2366
  reviewStack: (nodeToSave.reviewStack || []).map((r) => ({
2268
2367
  ...r,
@@ -2270,21 +2369,56 @@ var userActionHandlers = {
2270
2369
  })),
2271
2370
  recurrence_config: nextRecurrenceConfig
2272
2371
  };
2273
- specificParentData.nodes.push(nextQuestNode);
2372
+ const rootId = ((_d = nodeToSave.version_node) == null ? void 0 : _d.parent_node) || nodeToSave.id;
2373
+ const nextQuestLink = {
2374
+ id: `link_${short.generate()}`,
2375
+ source: rootId,
2376
+ target: nextQuestNode.id
2377
+ };
2378
+ await context.actions.upsert_quest_firestore(
2379
+ context.ownerId,
2380
+ context.sceneConfigId,
2381
+ nextQuestNode
2382
+ );
2383
+ await context.actions.upsert_quest_link_firestore(
2384
+ context.ownerId,
2385
+ context.sceneConfigId,
2386
+ nextQuestLink
2387
+ );
2388
+ await context.actions.update_view_counter(
2389
+ context.ownerId,
2390
+ context.sceneConfigId,
2391
+ nextCounter
2392
+ );
2393
+ if (specificParentData.links) {
2394
+ specificParentData.links.push(nextQuestLink);
2395
+ }
2396
+ if (specificParentData.nodes) {
2397
+ specificParentData.nodes.push(nextQuestNode);
2398
+ }
2399
+ if (isView && parentFileId === context.sceneConfigId) {
2400
+ if (!sceneDataRef.current.links) sceneDataRef.current.links = [];
2401
+ sceneDataRef.current.links.push(nextQuestLink);
2402
+ sceneDataRef.current.quest_nodes = [...specificParentData.nodes];
2403
+ }
2274
2404
  if (stateRef.current.nodeObjects[String(nodeToSave.id)]) {
2275
2405
  const oldMeshPosition = stateRef.current.nodeObjects[String(nodeToSave.id)].position.clone();
2276
2406
  setTimeout(() => {
2277
2407
  if (stateRef.current && stateRef.current.graphGroup) {
2278
2408
  const pos = oldMeshPosition.clone();
2279
2409
  pos.x += 12;
2280
- addStandaloneNodeToScene(stateRef.current, nextQuestNode, pos, true);
2410
+ addStandaloneNodeToScene(
2411
+ stateRef.current,
2412
+ nextQuestNode,
2413
+ pos,
2414
+ true
2415
+ );
2281
2416
  }
2282
2417
  }, 2e3);
2283
2418
  }
2284
2419
  }
2285
2420
  }
2286
2421
  try {
2287
- const isView = ((_c = context.viewType) == null ? void 0 : _c.toLowerCase()) === "view";
2288
2422
  if (isView && parentFileId === context.sceneConfigId) {
2289
2423
  const visualNodeIndex = sceneDataRef.current.nodes.findIndex(
2290
2424
  (n) => String(n.id) === String(nodeToSave.id)
@@ -2298,14 +2432,24 @@ var userActionHandlers = {
2298
2432
  const viewFilePayload = {
2299
2433
  parent_dbs: sceneDataRef.current.parent_dbs,
2300
2434
  nodes: sceneDataRef.current.nodes,
2301
- links: sceneDataRef.current.links,
2302
- quest_counter: sceneDataRef.current.quest_counter || 1
2435
+ links: sceneDataRef.current.links
2436
+ };
2437
+ const questFilePayload = {
2438
+ nodes: specificParentData.nodes,
2439
+ links: specificParentData.links,
2440
+ milestones: sceneDataRef.current.milestones || []
2303
2441
  };
2304
2442
  await context.actions.save_view_data(
2305
2443
  context.sceneSaveUrl,
2306
2444
  viewFilePayload
2307
2445
  );
2308
- await context.actions.save_quest(ownerId, context.sceneConfigId, nodeToSave);
2446
+ if (context.actions.upsert_quest_firestore) {
2447
+ await context.actions.upsert_quest_firestore(
2448
+ context.ownerId,
2449
+ context.sceneConfigId,
2450
+ nodeToSave
2451
+ );
2452
+ }
2309
2453
  } else {
2310
2454
  const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2311
2455
  await context.actions.save_view_data(
@@ -2316,7 +2460,6 @@ var userActionHandlers = {
2316
2460
  graphDataRef.current[parentFileId] = specificParentData;
2317
2461
  if (isView && parentFileId === context.sceneConfigId) {
2318
2462
  sceneDataRef.current.quest_nodes = [...specificParentData.nodes];
2319
- sceneDataRef.current.quest_counter = specificParentData.quest_counter || sceneDataRef.current.quest_counter;
2320
2463
  }
2321
2464
  updateExistingNodeVisuals(stateRef.current, nodeToSave);
2322
2465
  setters.setSceneVersion((v) => v + 1);
@@ -2373,8 +2516,7 @@ var userActionHandlers = {
2373
2516
  const viewFilePayload = {
2374
2517
  parent_dbs: sceneDataRef.current.parent_dbs,
2375
2518
  nodes: sceneDataRef.current.nodes,
2376
- links: sceneDataRef.current.links,
2377
- quest_counter: sceneDataRef.current.quest_counter || 1
2519
+ links: sceneDataRef.current.links
2378
2520
  };
2379
2521
  const questFilePayload = {
2380
2522
  nodes: specificParentData.nodes,
@@ -2385,8 +2527,13 @@ var userActionHandlers = {
2385
2527
  context.sceneSaveUrl,
2386
2528
  viewFilePayload
2387
2529
  );
2388
- const questSaveUrl = context.sceneSaveUrl.replace("x_view_scenes", "x_view_quests");
2389
- await context.actions.save_view_data(questSaveUrl, questFilePayload);
2530
+ if (context.actions.upsert_quest_link_firestore) {
2531
+ await context.actions.upsert_quest_link_firestore(
2532
+ context.ownerId,
2533
+ context.sceneConfigId,
2534
+ linkToSave
2535
+ );
2536
+ }
2390
2537
  } else {
2391
2538
  const filenameForSpecificParent = `x_view_dbs/${ownerId}/${parentFileId}`;
2392
2539
  await context.actions.save_view_data(
@@ -2481,7 +2628,7 @@ function createNewCustomProperty(existingProps = []) {
2481
2628
  nextValue = [{ id: uuidv4(), name: "1", value: "" }];
2482
2629
  break;
2483
2630
  case "date":
2484
- nextKey = "Date";
2631
+ nextKey = "Data";
2485
2632
  nextValue = { type: "Data", value: "" };
2486
2633
  break;
2487
2634
  case "number":
@@ -2735,7 +2882,7 @@ var IGNORED_KEYS = [
2735
2882
  ];
2736
2883
  function extractCustomPropsFromNode(node) {
2737
2884
  const customPropTypes = node._customPropTypes || {};
2738
- return Object.keys(node).filter((k) => !IGNORED_KEYS.includes(k)).map((key) => {
2885
+ return Object.keys(node).filter((k) => !IGNORED_KEYS.includes(k) || customPropTypes[k]).map((key) => {
2739
2886
  const val = node[key];
2740
2887
  const t = customPropTypes[key] || inferPropType(val);
2741
2888
  if (t === "date") {
@@ -3251,6 +3398,17 @@ var parseDate = (str) => {
3251
3398
  }
3252
3399
  return null;
3253
3400
  };
3401
+ function extractDatePropFromNode(node) {
3402
+ if (!node) return null;
3403
+ if (node.Date) return node.Date;
3404
+ if (node._customPropTypes) {
3405
+ const dateKey = Object.keys(node._customPropTypes).find(
3406
+ (k) => node._customPropTypes[k] === "date"
3407
+ );
3408
+ if (dateKey && node[dateKey]) return node[dateKey];
3409
+ }
3410
+ return null;
3411
+ }
3254
3412
  var parseDateProp = (dateProp) => {
3255
3413
  let info = { isUndated: true, label: "", type: "point" };
3256
3414
  if (!dateProp) return info;
@@ -4924,7 +5082,7 @@ function CustomPropertyDisplay({
4924
5082
  links: "Links",
4925
5083
  images: "Images",
4926
5084
  documents: "Documents",
4927
- date: "Date"
5085
+ date: "Data"
4928
5086
  };
4929
5087
  const oldKeyIsFixed = Object.values(fixedKeyMap).includes(tempProp.key);
4930
5088
  if (fixedKeyMap[value]) {
@@ -8789,7 +8947,7 @@ function CreateAncestryPanel({
8789
8947
  }
8790
8948
 
8791
8949
  // src/components/CreateQuestTreePanel.jsx
8792
- import React11, { useState as useState12, useEffect as useEffect11, useCallback as useCallback3, useMemo as useMemo9 } from "react";
8950
+ import React11, { useState as useState12, useEffect as useEffect11, useCallback as useCallback3, useMemo as useMemo9, useRef as useRef9 } from "react";
8793
8951
  import {
8794
8952
  FiFolder as FiFolder3,
8795
8953
  FiLayers as FiLayers6,
@@ -8906,6 +9064,7 @@ function CreateQuestTreePanel({
8906
9064
  const [isSaving, setIsSaving] = useState12(false);
8907
9065
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState12(false);
8908
9066
  const [activeTab, setActiveTab] = useState12("tree");
9067
+ const isInitialLoad = useRef9(true);
8909
9068
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8910
9069
  const { width: panelWidth, isResizing, handlePointerDown: handleResize } = useResizablePanel({
8911
9070
  initialWidth: 440,
@@ -8939,10 +9098,11 @@ function CreateQuestTreePanel({
8939
9098
  const indexToRemove = pathToRemove[pathToRemove.length - 1];
8940
9099
  if (currentParent.children && currentParent.children.length > indexToRemove) {
8941
9100
  currentParent.children.splice(indexToRemove, 1);
8942
- setHasUnsavedChanges(true);
9101
+ return { ...prev, [treeKey]: newTree };
8943
9102
  }
8944
- return { ...prev, [treeKey]: newTree };
9103
+ return prev;
8945
9104
  });
9105
+ setHasUnsavedChanges(true);
8946
9106
  }, [setQuestTreeMode, activeTab]);
8947
9107
  const handleMoveNode = (sourceId, targetId) => {
8948
9108
  const isAbstraction = activeTab === "abstraction";
@@ -9019,6 +9179,15 @@ function CreateQuestTreePanel({
9019
9179
  }));
9020
9180
  }
9021
9181
  }, [tree, abstraction_tree, questTreeMode.questNodeId, setQuestTreeMode]);
9182
+ useEffect11(() => {
9183
+ if (tree || abstraction_tree) {
9184
+ if (isInitialLoad.current) {
9185
+ isInitialLoad.current = false;
9186
+ return;
9187
+ }
9188
+ setHasUnsavedChanges(true);
9189
+ }
9190
+ }, [tree, abstraction_tree]);
9022
9191
  return /* @__PURE__ */ React11.createElement(
9023
9192
  "div",
9024
9193
  {
@@ -9209,11 +9378,11 @@ function ImageViewer({ data, onClose }) {
9209
9378
  }
9210
9379
 
9211
9380
  // src/components/InSceneCreationForm.jsx
9212
- import React15, { useState as useState15, useEffect as useEffect14, useRef as useRef10 } from "react";
9381
+ import React15, { useState as useState15, useEffect as useEffect14, useRef as useRef11 } from "react";
9213
9382
  import { v4 as uuidv45 } from "uuid";
9214
9383
 
9215
9384
  // src/components/ColorPicker.jsx
9216
- import React13, { useState as useState14, useEffect as useEffect13, useRef as useRef9 } from "react";
9385
+ import React13, { useState as useState14, useEffect as useEffect13, useRef as useRef10 } from "react";
9217
9386
  import { HexColorPicker } from "react-colorful";
9218
9387
  import { FiHash, FiCheck as FiCheck6 } from "react-icons/fi";
9219
9388
  var PRESET_COLORS = [
@@ -9235,7 +9404,7 @@ var PRESET_COLORS = [
9235
9404
  ];
9236
9405
  function ColorPicker({ color, onChange, disabled, isCompact = false }) {
9237
9406
  const [isOpen, setIsOpen] = useState14(false);
9238
- const popoverRef = useRef9(null);
9407
+ const popoverRef = useRef10(null);
9239
9408
  useEffect13(() => {
9240
9409
  const handleClickOutside = (event) => {
9241
9410
  if (popoverRef.current && !popoverRef.current.contains(event.target)) {
@@ -9462,7 +9631,7 @@ function InSceneCreationForm({
9462
9631
  const [isDatasetDropdownOpen, setIsDatasetDropdownOpen] = useState15(false);
9463
9632
  const [isLoadingDocs, setIsLoadingDocs] = useState15(false);
9464
9633
  const [docError, setDocError] = useState15(null);
9465
- const datasetDropdownRef = useRef10(null);
9634
+ const datasetDropdownRef = useRef11(null);
9466
9635
  useEffect14(() => {
9467
9636
  if (sourceNodeDatasetId) setTargetDatasetId(sourceNodeDatasetId);
9468
9637
  }, [sourceNodeDatasetId]);
@@ -9479,7 +9648,7 @@ function InSceneCreationForm({
9479
9648
  document.removeEventListener("mousedown", handleClickOutside);
9480
9649
  };
9481
9650
  }, [isDatasetDropdownOpen]);
9482
- const propsEndRef = useRef10(null);
9651
+ const propsEndRef = useRef11(null);
9483
9652
  const hasImages = customProps.some(
9484
9653
  (p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value)
9485
9654
  );
@@ -9982,7 +10151,7 @@ function InSceneCreationForm({
9982
10151
  }
9983
10152
 
9984
10153
  // src/components/InSceneVersionForm.jsx
9985
- import React16, { useState as useState16, useEffect as useEffect15, useRef as useRef11 } from "react";
10154
+ import React16, { useState as useState16, useEffect as useEffect15, useRef as useRef12 } from "react";
9986
10155
  import { v4 as uuidv46 } from "uuid";
9987
10156
  import { FiPlus as FiPlus5, FiMaximize2 as FiMaximize22, FiCheck as FiCheck8, FiEdit2 as FiEdit25 } from "react-icons/fi";
9988
10157
  function InSceneVersionForm({
@@ -10014,7 +10183,7 @@ function InSceneVersionForm({
10014
10183
  }
10015
10184
  ]);
10016
10185
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
10017
- const propsEndRef = useRef11(null);
10186
+ const propsEndRef = useRef12(null);
10018
10187
  const [useImageAsTexture, setUseImageAsTexture] = useState16(false);
10019
10188
  const [selectedImageUrl, setSelectedImageUrl] = useState16(null);
10020
10189
  const hasImages = customProps.some(
@@ -10298,7 +10467,7 @@ function InSceneVersionForm({
10298
10467
  }
10299
10468
 
10300
10469
  // src/components/InSceneQuestForm.jsx
10301
- import React18, { useState as useState18, useRef as useRef12 } from "react";
10470
+ import React18, { useState as useState18, useRef as useRef13 } from "react";
10302
10471
  import { v4 as uuidv47 } from "uuid";
10303
10472
  import {
10304
10473
  FiPlus as FiPlus7,
@@ -10537,7 +10706,7 @@ function InSceneQuestForm({
10537
10706
  const [docError, setDocError] = useState18(null);
10538
10707
  const [customProps, setCustomProps] = useState18([]);
10539
10708
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState18(false);
10540
- const propsEndRef = useRef12(null);
10709
+ const propsEndRef = useRef13(null);
10541
10710
  const [milestoneInput, setMilestoneInput] = useState18("");
10542
10711
  const [showMilestoneSuggestions, setShowMilestoneSuggestions] = useState18(false);
10543
10712
  const activeMilestones = React18.useMemo(
@@ -11332,7 +11501,7 @@ function InSceneQuestForm({
11332
11501
  }
11333
11502
 
11334
11503
  // src/components/NodeDetailsPanel.jsx
11335
- import React19, { useState as useState19, useEffect as useEffect16, useRef as useRef13 } from "react";
11504
+ import React19, { useState as useState19, useEffect as useEffect16, useRef as useRef14 } from "react";
11336
11505
  import { v4 as uuidv48 } from "uuid";
11337
11506
  import { FiPlus as FiPlus8, FiMaximize2 as FiMaximize23, FiX as FiX6, FiCheck as FiCheck10, FiImage as FiImage3, FiEdit2 as FiEdit27, FiLoader as FiLoader2, FiBookOpen as FiBookOpen3, FiSun as FiSun2, FiLink as FiLink5, FiDatabase } from "react-icons/fi";
11338
11507
  import { AiOutlineLoading3Quarters as AiOutlineLoading3Quarters3 } from "react-icons/ai";
@@ -11392,8 +11561,8 @@ function NodeDetailsPanel({
11392
11561
  useEffect16(() => {
11393
11562
  setWidth(isReadMode ? 700 : 440);
11394
11563
  }, [isReadMode, setWidth]);
11395
- const prevNodeIdRef = useRef13(null);
11396
- const propsEndRef = useRef13(null);
11564
+ const prevNodeIdRef = useRef14(null);
11565
+ const propsEndRef = useRef14(null);
11397
11566
  const canEdit = userRole !== "viewer";
11398
11567
  const availableImages = customProps.filter((p) => p.type === "images").flatMap((p) => Array.isArray(p.value) ? p.value : []).filter((img) => img.value && img.value.trim() !== "");
11399
11568
  const handleImageClickFromText = (url, name2) => {
@@ -11958,7 +12127,7 @@ function NodeDetailsPanel({
11958
12127
  }
11959
12128
 
11960
12129
  // src/components/QuestDetailsPanel.jsx
11961
- import React21, { useState as useState20, useEffect as useEffect17, useRef as useRef14 } from "react";
12130
+ import React21, { useState as useState20, useEffect as useEffect17, useRef as useRef15 } from "react";
11962
12131
  import { v4 as uuidv49 } from "uuid";
11963
12132
  import { FiPlus as FiPlus9, FiX as FiX7, FiCheck as FiCheck11, FiEdit2 as FiEdit28, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4, FiLink as FiLink7, FiTarget as FiTarget3, FiChevronDown as FiChevronDown7, FiUser as FiUser3, FiSearch as FiSearch7, FiRepeat as FiRepeat2, FiCalendar as FiCalendar2, FiImage as FiImage4, FiCopy as FiCopy4, FiGitBranch as FiGitBranch4 } from "react-icons/fi";
11964
12133
 
@@ -12226,8 +12395,8 @@ function QuestDetailsPanel({
12226
12395
  useEffect17(() => {
12227
12396
  setWidth(isReadMode ? 700 : 440);
12228
12397
  }, [isReadMode, setWidth]);
12229
- const prevNodeIdRef = useRef14(null);
12230
- const propsEndRef = useRef14(null);
12398
+ const prevNodeIdRef = useRef15(null);
12399
+ const propsEndRef = useRef15(null);
12231
12400
  const canEdit = userRole !== "viewer";
12232
12401
  const availableImages = customProps.filter((p) => p.type === "images").flatMap((p) => Array.isArray(p.value) ? p.value : []).filter((img) => img.value && img.value.trim() !== "");
12233
12402
  const handleImageClickFromText = (url, name) => {
@@ -12369,7 +12538,6 @@ function QuestDetailsPanel({
12369
12538
  setIsSaving(false);
12370
12539
  }
12371
12540
  };
12372
- const filteredCustomProps = customProps.filter((p) => p.key !== "Date" && p.key !== "Data");
12373
12541
  const handleAddTimelineNote = async () => {
12374
12542
  if (!noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving) return;
12375
12543
  setIsSaving(true);
@@ -13378,7 +13546,7 @@ ${noteText}` : noteText : entry.notes,
13378
13546
  }
13379
13547
 
13380
13548
  // src/components/MultiNodeContextMenu.jsx
13381
- import React22, { useLayoutEffect as useLayoutEffect3, useRef as useRef15, useState as useState21, useEffect as useEffect18 } from "react";
13549
+ import React22, { useLayoutEffect as useLayoutEffect3, useRef as useRef16, useState as useState21, useEffect as useEffect18 } from "react";
13382
13550
  function MultiNodeContextMenu({
13383
13551
  data,
13384
13552
  userRole,
@@ -13387,7 +13555,7 @@ function MultiNodeContextMenu({
13387
13555
  onDismissOtherNodes,
13388
13556
  onDeleteNodes
13389
13557
  }) {
13390
- const menuRef = useRef15(null);
13558
+ const menuRef = useRef16(null);
13391
13559
  const [menuPos, setMenuPos] = useState21({ left: 0, top: 0 });
13392
13560
  const [isConfirmingDelete, setIsConfirmingDelete] = useState21(false);
13393
13561
  const ability = defineAbilityFor(userRole);
@@ -13454,7 +13622,7 @@ function MultiNodeContextMenu({
13454
13622
  }
13455
13623
 
13456
13624
  // src/components/RelationshipDetailsPanel.jsx
13457
- import React23, { useState as useState22, useEffect as useEffect19, useRef as useRef16, useMemo as useMemo10 } from "react";
13625
+ import React23, { useState as useState22, useEffect as useEffect19, useRef as useRef17, useMemo as useMemo10 } from "react";
13458
13626
  import { v4 as uuidv410 } from "uuid";
13459
13627
  import { FiPlus as FiPlus10, FiEdit2 as FiEdit29, FiLoader as FiLoader4, FiBookOpen as FiBookOpen5 } from "react-icons/fi";
13460
13628
  function RelationshipDetailsPanel({
@@ -13485,7 +13653,7 @@ function RelationshipDetailsPanel({
13485
13653
  const [isSaving, setIsSaving] = useState22(false);
13486
13654
  const [isReadMode, setIsReadMode] = useState22(false);
13487
13655
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState22(false);
13488
- const propsEndRef = useRef16(null);
13656
+ const propsEndRef = useRef17(null);
13489
13657
  const canEdit = useMemo10(() => {
13490
13658
  const ability = defineAbilityFor(userRole);
13491
13659
  return ability.can("update", "Connection");
@@ -13806,7 +13974,7 @@ function RelationshipDetailsPanel({
13806
13974
  }
13807
13975
 
13808
13976
  // src/components/RelationshipContextMenu.jsx
13809
- import React24, { useLayoutEffect as useLayoutEffect4, useRef as useRef17, useState as useState23, useEffect as useEffect20, useMemo as useMemo11 } from "react";
13977
+ import React24, { useLayoutEffect as useLayoutEffect4, useRef as useRef18, useState as useState23, useEffect as useEffect20, useMemo as useMemo11 } from "react";
13810
13978
  function RelationshipContextMenu({
13811
13979
  data,
13812
13980
  userRole,
@@ -13816,7 +13984,7 @@ function RelationshipContextMenu({
13816
13984
  onDelete,
13817
13985
  onClose
13818
13986
  }) {
13819
- const menuRef = useRef17(null);
13987
+ const menuRef = useRef18(null);
13820
13988
  const [menuPos, setMenuPos] = useState23({ left: 0, top: 0 });
13821
13989
  const [isConfirmingDelete, setIsConfirmingDelete] = useState23(false);
13822
13990
  const ability = useMemo11(() => defineAbilityFor(userRole), [userRole]);
@@ -14298,7 +14466,7 @@ import React30, {
14298
14466
  useState as useState28,
14299
14467
  useMemo as useMemo13,
14300
14468
  useEffect as useEffect24,
14301
- useRef as useRef20,
14469
+ useRef as useRef21,
14302
14470
  useCallback as useCallback6
14303
14471
  } from "react";
14304
14472
  import {
@@ -14324,10 +14492,10 @@ import {
14324
14492
  import { v4 as uuidv411 } from "uuid";
14325
14493
 
14326
14494
  // src/components/RichGroupEditor.jsx
14327
- import React29, { useEffect as useEffect23, useRef as useRef19, useState as useState27, useCallback as useCallback5 } from "react";
14495
+ import React29, { useEffect as useEffect23, useRef as useRef20, useState as useState27, useCallback as useCallback5 } from "react";
14328
14496
 
14329
14497
  // src/components/MentionPopover.jsx
14330
- import React28, { useEffect as useEffect22, useRef as useRef18, useState as useState26, useMemo as useMemo12 } from "react";
14498
+ import React28, { useEffect as useEffect22, useRef as useRef19, useState as useState26, useMemo as useMemo12 } from "react";
14331
14499
  import { FiLayers as FiLayers7, FiCheckSquare as FiCheckSquare3, FiGitBranch as FiGitBranch5, FiBox } from "react-icons/fi";
14332
14500
  var TYPE_CONFIG = {
14333
14501
  ancestry: { Icon: FiLayers7, color: "text-indigo-400", label: "Ancestralidade" },
@@ -14337,7 +14505,7 @@ var TYPE_CONFIG = {
14337
14505
  };
14338
14506
  function MentionPopover({ query, items, position, onSelect, onClose }) {
14339
14507
  const [activeIndex, setActiveIndex] = useState26(0);
14340
- const listRef = useRef18(null);
14508
+ const listRef = useRef19(null);
14341
14509
  const filtered = useMemo12(() => {
14342
14510
  const term = (query || "").toLowerCase().trim();
14343
14511
  if (!term) return items.slice(0, 8);
@@ -14477,9 +14645,9 @@ var RichGroupEditor = React29.memo(function RichGroupEditor2({
14477
14645
  readOnly,
14478
14646
  placeholder
14479
14647
  }) {
14480
- const editorRef = useRef19(null);
14481
- const isMountedRef = useRef19(false);
14482
- const dismissedTriggerRef = useRef19(null);
14648
+ const editorRef = useRef20(null);
14649
+ const isMountedRef = useRef20(false);
14650
+ const dismissedTriggerRef = useRef20(null);
14483
14651
  const [mentionState, setMentionState] = useState27(null);
14484
14652
  const [isEmpty, setIsEmpty] = useState27(true);
14485
14653
  useEffect23(() => {
@@ -14947,7 +15115,7 @@ function Boards({
14947
15115
  const [isLoaded, setIsLoaded] = useState28(false);
14948
15116
  const [saveStatus, setSaveStatus] = useState28("idle");
14949
15117
  const [navigationStack, setNavigationStack] = useState28([]);
14950
- const lastSavedBoardItemsRef = useRef20(null);
15118
+ const lastSavedBoardItemsRef = useRef21(null);
14951
15119
  const canEdit = useMemo13(() => userRole !== "viewer", [userRole]);
14952
15120
  const findGroupAndParent = (list, targetId, parent = null) => {
14953
15121
  for (let i = 0; i < list.length; i++) {
@@ -15519,12 +15687,12 @@ function XViewScene({
15519
15687
  get_view_members,
15520
15688
  create_google_doc_action,
15521
15689
  open_google_picker_action,
15522
- save_quest_action,
15523
- delete_quest_action,
15524
- save_quest_link_action,
15525
- delete_quest_link_action,
15526
- get_view_quests_and_links_action,
15527
- migrate_legacy_quests_action
15690
+ upsert_quest_firestore,
15691
+ delete_quest_firestore,
15692
+ upsert_quest_link_firestore,
15693
+ delete_quest_link_firestore,
15694
+ upsert_milestone_firestore,
15695
+ update_view_counter
15528
15696
  }) {
15529
15697
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
15530
15698
  const { data: session, status } = useSession();
@@ -15598,10 +15766,10 @@ function XViewScene({
15598
15766
  }
15599
15767
  return null;
15600
15768
  }, [ownerId, sceneConfigId]);
15601
- const sceneDataRef = useRef21(null);
15602
- const parentDataRef = useRef21(null);
15769
+ const sceneDataRef = useRef22(null);
15770
+ const parentDataRef = useRef22(null);
15603
15771
  const graphDataRef = parentDataRef;
15604
- const ancestryDataRef = useRef21(null);
15772
+ const ancestryDataRef = useRef22(null);
15605
15773
  const [isLoading, setIsLoading] = useState30(true);
15606
15774
  const [permissionStatus, setPermissionStatus] = useState30("loading");
15607
15775
  const [userPermissionRole, setUserPermissionRole] = useState30(null);
@@ -15690,10 +15858,10 @@ function XViewScene({
15690
15858
  const [boardItems, setBoardItems] = useState30([]);
15691
15859
  const [isSidebarOpen, setIsSidebarOpen] = useState30(false);
15692
15860
  const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = useState30(false);
15693
- const mountRef = useRef21(null);
15694
- const tooltipRef = useRef21(null);
15695
- const formRef = useRef21(null);
15696
- const stateRef = useRef21({
15861
+ const mountRef = useRef22(null);
15862
+ const tooltipRef = useRef22(null);
15863
+ const formRef = useRef22(null);
15864
+ const stateRef = useRef22({
15697
15865
  readMode: {
15698
15866
  currentMaxIndex: 0,
15699
15867
  progressMap: {}
@@ -16083,12 +16251,20 @@ function XViewScene({
16083
16251
  obj.material.dispose();
16084
16252
  }
16085
16253
  }
16086
- Object.values(nodeObjects || {}).forEach((mesh) => {
16087
- const oldLabel = mesh.getObjectByName("timelineLabel");
16088
- if (oldLabel) {
16089
- mesh.remove(oldLabel);
16090
- if (oldLabel.material.map) oldLabel.material.map.dispose();
16091
- oldLabel.material.dispose();
16254
+ Object.values(nodeObjects).forEach((mesh) => {
16255
+ const oldTimelineLabel = mesh.getObjectByName("timelineLabel");
16256
+ if (oldTimelineLabel) {
16257
+ mesh.remove(oldTimelineLabel);
16258
+ if (oldTimelineLabel.material.map)
16259
+ oldTimelineLabel.material.map.dispose();
16260
+ oldTimelineLabel.material.dispose();
16261
+ }
16262
+ const oldQuestDateLabel = mesh.getObjectByName("questDateLabel");
16263
+ if (oldQuestDateLabel) {
16264
+ mesh.remove(oldQuestDateLabel);
16265
+ if (oldQuestDateLabel.material.map)
16266
+ oldQuestDateLabel.material.map.dispose();
16267
+ oldQuestDateLabel.material.dispose();
16092
16268
  }
16093
16269
  delete mesh.userData.timelineIntervalBar;
16094
16270
  delete mesh.userData.timelineEndLabel;
@@ -16107,43 +16283,19 @@ function XViewScene({
16107
16283
  }
16108
16284
  return null;
16109
16285
  };
16110
- Object.values(nodeObjects || {}).forEach((mesh) => {
16111
- const dateProp = mesh.userData.Date;
16286
+ Object.values(nodeObjects).forEach((mesh) => {
16287
+ const dateProp = extractDatePropFromNode(mesh.userData);
16112
16288
  let nodeInfo = { mesh, type: "point", label: "", isUndated: true };
16113
16289
  if (dateProp) {
16114
- if (dateProp.date) {
16115
- const date = parseDate2(dateProp.date);
16116
- if (date) {
16117
- nodeInfo.startDate = date;
16118
- nodeInfo.endDate = date;
16119
- nodeInfo.label = dateProp.date;
16120
- nodeInfo.isUndated = false;
16121
- allTimePoints.add(date.getTime());
16122
- }
16123
- } else if (dateProp.year) {
16124
- const year = parseInt(dateProp.year, 10);
16125
- if (!isNaN(year)) {
16126
- const date = new Date(year, 0, 1);
16127
- nodeInfo.startDate = date;
16128
- nodeInfo.endDate = date;
16129
- nodeInfo.label = String(year);
16130
- nodeInfo.isUndated = false;
16131
- allTimePoints.add(date.getTime());
16132
- }
16133
- } else if (dateProp.date_interval && dateProp.date_interval.start) {
16134
- const startYear = parseInt(dateProp.date_interval.start, 10);
16135
- const endYear = parseInt(dateProp.date_interval.end, 10) || startYear;
16136
- if (!isNaN(startYear)) {
16137
- const startDate = new Date(startYear, 0, 1);
16138
- const endDate = new Date(endYear, 11, 31);
16139
- nodeInfo.type = "interval";
16140
- nodeInfo.startDate = startDate;
16141
- nodeInfo.endDate = endDate;
16142
- nodeInfo.label = `${startYear} - ${endYear}`;
16143
- nodeInfo.isUndated = false;
16144
- allTimePoints.add(startDate.getTime());
16145
- allTimePoints.add(endDate.getTime());
16146
- }
16290
+ const info = parseDateProp(dateProp);
16291
+ if (!info.isUndated) {
16292
+ nodeInfo.startDate = info.startDate;
16293
+ nodeInfo.endDate = info.endDate;
16294
+ nodeInfo.label = info.label;
16295
+ nodeInfo.type = info.type;
16296
+ nodeInfo.isUndated = false;
16297
+ allTimePoints.add(info.startDate.getTime());
16298
+ if (info.endDate) allTimePoints.add(info.endDate.getTime());
16147
16299
  }
16148
16300
  }
16149
16301
  timelineNodes.push(nodeInfo);
@@ -16271,50 +16423,29 @@ function XViewScene({
16271
16423
  return null;
16272
16424
  };
16273
16425
  versionMeshes.forEach((mesh) => {
16274
- const dateProp = mesh.userData.Date;
16426
+ const dateProp = extractDatePropFromNode(mesh.userData);
16275
16427
  if (!dateProp) return;
16276
- let nodeInfo = { mesh, type: "point", label: "" };
16277
- if (dateProp.date) {
16278
- const date = parseDate2(dateProp.date);
16279
- if (date) {
16280
- nodeInfo.startDate = date;
16281
- nodeInfo.endDate = date;
16282
- nodeInfo.label = dateProp.date;
16283
- allTimePoints.add(date.getTime());
16284
- timelineNodes.push(nodeInfo);
16285
- }
16286
- } else if (dateProp.year) {
16287
- const year = parseInt(dateProp.year, 10);
16288
- if (!isNaN(year)) {
16289
- const date = new Date(year, 0, 1);
16290
- nodeInfo.startDate = date;
16291
- nodeInfo.endDate = date;
16292
- nodeInfo.label = String(year);
16293
- allTimePoints.add(date.getTime());
16294
- timelineNodes.push(nodeInfo);
16295
- }
16296
- } else if (dateProp.date_interval && dateProp.date_interval.start) {
16297
- const startYear = parseInt(dateProp.date_interval.start, 10);
16298
- const endYear = parseInt(dateProp.date_interval.end, 10) || startYear;
16299
- if (!isNaN(startYear)) {
16300
- const startDate = new Date(startYear, 0, 1);
16301
- const endDate = new Date(endYear, 11, 31);
16302
- nodeInfo.type = "interval";
16303
- nodeInfo.startDate = startDate;
16304
- nodeInfo.endDate = endDate;
16305
- nodeInfo.label = `${startYear} - ${endYear}`;
16306
- allTimePoints.add(startDate.getTime());
16307
- allTimePoints.add(endDate.getTime());
16308
- timelineNodes.push(nodeInfo);
16309
- }
16428
+ const info = parseDateProp(dateProp);
16429
+ if (!info.isUndated) {
16430
+ const nodeInfo = {
16431
+ mesh,
16432
+ type: info.type,
16433
+ startDate: info.startDate,
16434
+ endDate: info.endDate,
16435
+ label: info.label
16436
+ };
16437
+ allTimePoints.add(info.startDate.getTime());
16438
+ if (info.endDate) allTimePoints.add(info.endDate.getTime());
16439
+ timelineNodes.push(nodeInfo);
16310
16440
  }
16311
16441
  });
16312
16442
  if (timelineNodes.length === 0) return;
16313
16443
  const sortedTimePoints = Array.from(allTimePoints).sort((a, b) => a - b);
16444
+ const maxTimeIndex = sortedTimePoints.length - 1;
16314
16445
  const timeToYMap = new Map(
16315
16446
  sortedTimePoints.map((time, index) => [
16316
16447
  time,
16317
- index * TIMELINE_LAYER_SPACING_Y
16448
+ (index - maxTimeIndex) * TIMELINE_LAYER_SPACING_Y
16318
16449
  ])
16319
16450
  );
16320
16451
  timelineNodes.sort(
@@ -16322,18 +16453,38 @@ function XViewScene({
16322
16453
  );
16323
16454
  const baseX = sourceMesh.position.x + TIMELINE_NODE_SPACING_X;
16324
16455
  const baseY = sourceMesh.position.y;
16325
- const maxNodeIndex = timelineNodes.length - 1;
16326
- timelineNodes.forEach((node, index) => {
16456
+ const columns = [];
16457
+ const allFinalPoints = [sourceMesh.position.clone()];
16458
+ timelineNodes.forEach((node) => {
16327
16459
  const { mesh, type, startDate, endDate, label } = node;
16328
- const y = baseY + (index - maxNodeIndex) * TIMELINE_LAYER_SPACING_Y;
16329
- const versionTween = new Tween2(mesh.position).to({ x: baseX, y, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
16460
+ const startY = timeToYMap.get(startDate.getTime());
16461
+ const endY = timeToYMap.get(endDate.getTime()) || startY;
16462
+ let foundColumn = false;
16463
+ for (let i = 0; i < columns.length; i++) {
16464
+ if (columns[i].endY < startY) {
16465
+ node.columnIndex = i;
16466
+ columns[i].endY = endY;
16467
+ foundColumn = true;
16468
+ break;
16469
+ }
16470
+ }
16471
+ if (!foundColumn) {
16472
+ node.columnIndex = columns.length;
16473
+ columns.push({ endY });
16474
+ }
16475
+ const x = baseX + node.columnIndex * TIMELINE_NODE_SPACING_X;
16476
+ const y = baseY + startY;
16477
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
16478
+ const versionTween = new Tween2(mesh.position).to({ x, y, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
16330
16479
  tweenGroup.add(versionTween);
16331
16480
  versionTween.start();
16332
- const startLabelText = type === "interval" ? startDate.getFullYear().toString() : label;
16333
- const startLabel = createTextSprite(startLabelText, 48);
16334
- startLabel.name = "timelineLabel";
16335
- startLabel.position.set(0, -2.5, 0);
16336
- mesh.add(startLabel);
16481
+ if (!mesh.getObjectByName("questDateLabel")) {
16482
+ const startLabelText = type === "interval" ? startDate.getFullYear().toString() : label;
16483
+ const startLabel = createTextSprite(startLabelText, 48);
16484
+ startLabel.name = "timelineLabel";
16485
+ startLabel.position.set(0, -2.5, 0);
16486
+ mesh.add(startLabel);
16487
+ }
16337
16488
  if (type === "interval") {
16338
16489
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
16339
16490
  if (relativeEndY > 0) {
@@ -16367,7 +16518,21 @@ function XViewScene({
16367
16518
  }
16368
16519
  }
16369
16520
  });
16370
- }, []);
16521
+ if (allFinalPoints.length > 0) {
16522
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
16523
+ const center = new THREE3.Vector3();
16524
+ boundingBox.getCenter(center);
16525
+ const size = new THREE3.Vector3();
16526
+ boundingBox.getSize(size);
16527
+ const spread = Math.max(size.x, size.y, size.z, 1);
16528
+ const desiredDistance = Math.max(
16529
+ x_view_config.CAMERA_ZOOM_DISTANCE,
16530
+ spread * 1.5
16531
+ );
16532
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
16533
+ tweenToTarget(center, zoomFactor);
16534
+ }
16535
+ }, [tweenToTarget]);
16371
16536
  useEffect25(() => {
16372
16537
  async function fetchAllData(configPath, ownerId2) {
16373
16538
  var _a2, _b2;
@@ -16388,40 +16553,19 @@ function XViewScene({
16388
16553
  focusAncestryId
16389
16554
  );
16390
16555
  const boardPromise = get_boards_action && session ? get_boards_action(configPath, sceneType, session, ownerId2) : Promise.resolve({ success: false, data: [] });
16391
- const questPromise = get_view_quests_and_links_action && session ? get_view_quests_and_links_action(ownerId2, sceneConfigId) : Promise.resolve({ success: false, quests: [], links: [] });
16392
- const [sceneResponse, boardResponse, questResponse] = await Promise.all([
16556
+ const [sceneResponse, boardResponse] = await Promise.all([
16393
16557
  scenePromise,
16394
- boardPromise,
16395
- questPromise
16558
+ boardPromise
16396
16559
  ]);
16397
16560
  if ((sceneResponse == null ? void 0 : sceneResponse.success) && ((_a2 = sceneResponse.data) == null ? void 0 : _a2.scene) && ((_b2 = sceneResponse.data) == null ? void 0 : _b2.parent)) {
16398
- const sceneData = sceneResponse.data.scene;
16399
- let firestoreQuests = (questResponse == null ? void 0 : questResponse.success) && Array.isArray(questResponse.quests) ? questResponse.quests : [];
16400
- let firestoreLinks = (questResponse == null ? void 0 : questResponse.success) && Array.isArray(questResponse.links) ? questResponse.links : [];
16401
- if (!sceneData.quests_migrated && firestoreQuests.length === 0) {
16402
- const legacyQuests = sceneData.quest_nodes || [];
16403
- const legacyLinks = sceneData.quest_links || [];
16404
- if ((legacyQuests.length > 0 || legacyLinks.length > 0) && migrate_legacy_quests_action) {
16405
- console.log(
16406
- "X-View: Iniciando Lazy Migration de Quests para Firestore..."
16407
- );
16408
- await migrate_legacy_quests_action(ownerId2, sceneConfigId, {
16409
- nodes: legacyQuests,
16410
- links: legacyLinks
16411
- });
16412
- firestoreQuests = legacyQuests;
16413
- firestoreLinks = legacyLinks;
16414
- sceneData.quests_migrated = true;
16415
- }
16416
- }
16417
16561
  if (focusNodeId) {
16418
16562
  let targetNode = sceneResponse.data.scene.nodes.find(
16419
16563
  (n) => String(n.id) === String(focusNodeId)
16420
16564
  );
16421
16565
  if (!targetNode) {
16422
16566
  const allParentNodes = Object.values(
16423
- sceneResponse.data.parent || {}
16424
- ).flatMap((f) => (f == null ? void 0 : f.nodes) || []);
16567
+ sceneResponse.data.parent
16568
+ ).flatMap((f) => f.nodes || []);
16425
16569
  targetNode = allParentNodes.find(
16426
16570
  (n) => String(n.id) === String(focusNodeId)
16427
16571
  );
@@ -16435,29 +16579,10 @@ function XViewScene({
16435
16579
  sceneResponse.data.scene.links = [];
16436
16580
  }
16437
16581
  sceneDataRef.current = {
16438
- ...sceneData,
16439
- quests_migrated: sceneData.quests_migrated || false,
16440
- milestones: sceneData.milestones || []
16582
+ ...sceneResponse.data.scene,
16583
+ milestones: sceneResponse.data.scene.milestones || []
16441
16584
  };
16442
- const currentParentData = sceneResponse.data.parent;
16443
- if (currentParentData && currentParentData[sceneConfigId]) {
16444
- const existingNodes = currentParentData[sceneConfigId].nodes || [];
16445
- const firestoreQuestIds = new Set(firestoreQuests.map((q) => String(q.id)));
16446
- const filteredExisting = existingNodes.filter(
16447
- (n) => !firestoreQuestIds.has(String(n.id))
16448
- );
16449
- currentParentData[sceneConfigId].nodes = [
16450
- ...filteredExisting,
16451
- ...firestoreQuests
16452
- ];
16453
- if (firestoreLinks && firestoreLinks.length > 0) {
16454
- currentParentData[sceneConfigId].links = [
16455
- ...currentParentData[sceneConfigId].links || [],
16456
- ...firestoreLinks
16457
- ];
16458
- }
16459
- }
16460
- parentDataRef.current = currentParentData;
16585
+ parentDataRef.current = sceneResponse.data.parent;
16461
16586
  ancestryDataRef.current = sceneResponse.data.ancestry;
16462
16587
  console.log(
16463
16588
  "Console de sceneResponse.data.scene:",
@@ -16763,7 +16888,7 @@ function XViewScene({
16763
16888
  positionGroups.get(posKey).push(String(q.id));
16764
16889
  });
16765
16890
  });
16766
- Object.values(nodeObjects || {}).forEach((mesh) => {
16891
+ Object.values(nodeObjects).forEach((mesh) => {
16767
16892
  const oldLabel = mesh.getObjectByName("questDateLabel") || mesh.getObjectByName("timelineLabel");
16768
16893
  if (oldLabel) {
16769
16894
  mesh.remove(oldLabel);
@@ -18072,12 +18197,13 @@ function XViewScene({
18072
18197
  get_scene_view_data,
18073
18198
  upload_file: upload_file_action,
18074
18199
  delete_file: delete_file_action,
18075
- save_quest: save_quest_action,
18076
- delete_quest: delete_quest_action,
18077
- save_quest_link: save_quest_link_action,
18078
- delete_quest_link: delete_quest_link_action,
18079
- get_view_quests_and_links: get_view_quests_and_links_action,
18080
- migrate_legacy_quests: migrate_legacy_quests_action
18200
+ // Adicionado novas importações aqui
18201
+ upsert_quest_firestore,
18202
+ delete_quest_firestore,
18203
+ upsert_quest_link_firestore,
18204
+ delete_quest_link_firestore,
18205
+ upsert_milestone_firestore,
18206
+ update_view_counter
18081
18207
  }
18082
18208
  };
18083
18209
  },
@@ -18097,12 +18223,12 @@ function XViewScene({
18097
18223
  get_scene_view_data,
18098
18224
  upload_file_action,
18099
18225
  delete_file_action,
18100
- save_quest_action,
18101
- delete_quest_action,
18102
- save_quest_link_action,
18103
- delete_quest_link_action,
18104
- get_view_quests_and_links_action,
18105
- migrate_legacy_quests_action
18226
+ upsert_quest_firestore,
18227
+ delete_quest_firestore,
18228
+ upsert_quest_link_firestore,
18229
+ delete_quest_link_firestore,
18230
+ upsert_milestone_firestore,
18231
+ update_view_counter
18106
18232
  ]
18107
18233
  );
18108
18234
  const handleStartVersioning = (nodeData) => {
@@ -18112,7 +18238,7 @@ function XViewScene({
18112
18238
  (questNode) => {
18113
18239
  var _a2, _b2, _c2;
18114
18240
  if (!questNode || !actionHandlerContext) return;
18115
- const allParentNodes = Object.values(parentDataRef.current || {}).flatMap(
18241
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
18116
18242
  (fileData) => fileData.nodes || []
18117
18243
  );
18118
18244
  const blockCheck = isQuestBlocked(questNode, allParentNodes);
@@ -18308,36 +18434,30 @@ function XViewScene({
18308
18434
  parentDataRef.current[sceneConfigId].nodes[parentIndex].quest_abstraction_tree = formattedAbstractionTree;
18309
18435
  }
18310
18436
  }
18311
- const sceneFileData = {
18312
- ...sceneData,
18313
- nodes: sceneData.nodes || [],
18314
- links: sceneData.links || [],
18315
- quest_counter: sceneData.quest_counter || 1
18316
- };
18317
- const questFileData = {
18318
- nodes: sceneData.quest_nodes || [],
18319
- links: sceneData.quest_links || [],
18320
- milestones: sceneData.milestones || []
18321
- };
18322
- await save_view_data(sceneSaveUrl, sceneFileData);
18323
- await save_quest_action(ownerId, sceneConfigId, updatedNodeData);
18324
- }
18325
- const updatedNodeData = sceneData.quest_nodes[questIndex];
18326
- const nodeMesh = (_a2 = stateRef.current.nodeObjects) == null ? void 0 : _a2[String(questNodeId)];
18327
- if (nodeMesh) {
18328
- nodeMesh.userData = {
18329
- ...nodeMesh.userData,
18330
- ...updatedNodeData
18331
- };
18332
- }
18333
- if (detailsNode && String(detailsNode.id) === String(questNodeId)) {
18334
- setDetailsNode((prev) => ({
18335
- ...prev,
18336
- quest_tree: formattedTree,
18337
- quest_abstraction_tree: formattedAbstractionTree
18338
- }));
18437
+ const updatedNodeData = sceneData.quest_nodes[questIndex];
18438
+ if (upsert_quest_firestore) {
18439
+ await upsert_quest_firestore(
18440
+ ownerId,
18441
+ sceneConfigId,
18442
+ updatedNodeData
18443
+ );
18444
+ }
18445
+ const nodeMesh = (_a2 = stateRef.current.nodeObjects) == null ? void 0 : _a2[String(questNodeId)];
18446
+ if (nodeMesh) {
18447
+ nodeMesh.userData = {
18448
+ ...nodeMesh.userData,
18449
+ ...updatedNodeData
18450
+ };
18451
+ }
18452
+ if (detailsNode && String(detailsNode.id) === String(questNodeId)) {
18453
+ setDetailsNode((prev) => ({
18454
+ ...prev,
18455
+ quest_tree: formattedTree,
18456
+ quest_abstraction_tree: formattedAbstractionTree
18457
+ }));
18458
+ }
18459
+ setSceneVersion((v) => v + 1);
18339
18460
  }
18340
- setSceneVersion((v) => v + 1);
18341
18461
  }
18342
18462
  }
18343
18463
  };
@@ -18386,7 +18506,6 @@ function XViewScene({
18386
18506
  ownerId: ownerId2
18387
18507
  } = context;
18388
18508
  if (!graphDataRef2.current || (viewType == null ? void 0 : viewType.toLowerCase()) !== "view") return;
18389
- const currentCounter = sceneDataRef2.current.quest_counter || 1;
18390
18509
  const newNode = {
18391
18510
  id: short2.generate(),
18392
18511
  ...newQuestData,
@@ -18397,19 +18516,16 @@ function XViewScene({
18397
18516
  graphDataRef2.current[sceneConfigId2] = { nodes: [], links: [] };
18398
18517
  }
18399
18518
  graphDataRef2.current[sceneConfigId2].nodes.push(newNode);
18400
- const sceneFileData = {
18401
- ...sceneDataRef2.current,
18402
- quest_counter: currentCounter + 1
18403
- };
18404
- const questFileData = {
18405
- nodes: graphDataRef2.current[sceneConfigId2].nodes,
18406
- links: graphDataRef2.current[sceneConfigId2].links,
18407
- milestones: sceneDataRef2.current.milestones || []
18408
- };
18519
+ const currentCounter = sceneDataRef2.current.quest_counter || 1;
18520
+ const nextCounter = currentCounter + 1;
18409
18521
  try {
18410
- await actions.save_view_data(sceneSaveUrl2, sceneFileData);
18411
- await actions.save_quest(ownerId2, sceneConfigId2, newNode);
18412
- sceneDataRef2.current.quest_counter = currentCounter + 1;
18522
+ if (actions.upsert_quest_firestore) {
18523
+ await actions.upsert_quest_firestore(ownerId2, sceneConfigId2, newNode);
18524
+ }
18525
+ if (actions.update_view_counter) {
18526
+ await actions.update_view_counter(ownerId2, sceneConfigId2, nextCounter);
18527
+ }
18528
+ sceneDataRef2.current.quest_counter = nextCounter;
18413
18529
  stateRef2.current.nodeIdToParentFileMap.set(String(newNode.id), {
18414
18530
  parentFileId: sceneConfigId2,
18415
18531
  ownerId: ownerId2,
@@ -18440,84 +18556,6 @@ function XViewScene({
18440
18556
  alert("Ocorreu um erro ao criar a Quest.");
18441
18557
  }
18442
18558
  };
18443
- userActionHandlers.handleCompleteConnection = async (context, targetNodeData) => {
18444
- const {
18445
- stateRef: stateRef2,
18446
- graphDataRef: graphDataRef2,
18447
- sceneDataRef: sceneDataRef2,
18448
- sceneConfigId: sceneConfigId2,
18449
- sceneSaveUrl: sceneSaveUrl2,
18450
- ownerId: ownerId2
18451
- } = context;
18452
- const { sourceNodeData } = stateRef2.current.connection;
18453
- if (!graphDataRef2.current || !sceneDataRef2.current || !sourceNodeData || !targetNodeData) {
18454
- userActionHandlers.handleCancelConnection(context);
18455
- return;
18456
- }
18457
- const sourceParentInfo = getParentFileInfoForNode(
18458
- graphDataRef2.current,
18459
- sceneDataRef2.current,
18460
- sourceNodeData.id,
18461
- sceneConfigId2,
18462
- ownerId2
18463
- );
18464
- const targetParentInfo = getParentFileInfoForNode(
18465
- graphDataRef2.current,
18466
- sceneDataRef2.current,
18467
- targetNodeData.id,
18468
- sceneConfigId2,
18469
- ownerId2
18470
- );
18471
- let parentInfoToSave = sourceParentInfo;
18472
- const isSourceQuest = sourceParentInfo.parentFileId === sceneConfigId2;
18473
- const isTargetQuest = targetParentInfo.parentFileId === sceneConfigId2;
18474
- if (isSourceQuest || isTargetQuest) {
18475
- parentInfoToSave = { parentFileId: sceneConfigId2, ownerId: ownerId2 };
18476
- }
18477
- const { parentFileId: parentFileIdToSave, ownerId: ownerIdToSave } = parentInfoToSave;
18478
- const newLink = {
18479
- id: `link_${short2.generate()}`,
18480
- source: sourceNodeData.id,
18481
- target: targetNodeData.id
18482
- };
18483
- try {
18484
- if (parentFileIdToSave === sceneConfigId2) {
18485
- const specificParentData = graphDataRef2.current[sceneConfigId2];
18486
- specificParentData.links.push(newLink);
18487
- const viewFilePayload = {
18488
- ...sceneDataRef2.current,
18489
- quest_counter: sceneDataRef2.current.quest_counter || 1
18490
- };
18491
- const questFilePayload = {
18492
- nodes: specificParentData.nodes,
18493
- links: specificParentData.links,
18494
- milestones: sceneDataRef2.current.milestones || []
18495
- };
18496
- await context.actions.save_view_data(sceneSaveUrl2, viewFilePayload);
18497
- const questSaveUrl = sceneSaveUrl2.replace(
18498
- "x_view_scenes",
18499
- "x_view_quests"
18500
- );
18501
- await context.actions.save_view_data(questSaveUrl, questFilePayload);
18502
- } else {
18503
- const specificParentData = JSON.parse(
18504
- JSON.stringify(graphDataRef2.current[parentFileIdToSave])
18505
- );
18506
- specificParentData.links.push(newLink);
18507
- const filenameForSpecificParent = `x_view_dbs/${ownerIdToSave}/${parentFileIdToSave}`;
18508
- await context.actions.save_view_data(
18509
- filenameForSpecificParent,
18510
- specificParentData
18511
- );
18512
- graphDataRef2.current[parentFileIdToSave] = specificParentData;
18513
- }
18514
- addNewLinkToScene(stateRef2.current, newLink);
18515
- } catch (error) {
18516
- console.error("Falha ao salvar a nova conex\xE3o:", error);
18517
- alert("Ocorreu um erro ao salvar a nova conex\xE3o.");
18518
- }
18519
- userActionHandlers.handleCancelConnection(context);
18520
- };
18521
18559
  const handleClearAncestryVisuals = useCallback7((ancestryId) => {
18522
18560
  const { renderedAncestries, ancestryGroup } = stateRef.current;
18523
18561
  const renderIndex = renderedAncestries.findIndex(
@@ -18649,7 +18687,7 @@ function XViewScene({
18649
18687
  if (line.material) line.material.dispose();
18650
18688
  });
18651
18689
  }
18652
- const allParentNodes = Object.values(parentDataRef.current || {}).flatMap(
18690
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
18653
18691
  (fileData) => fileData.nodes
18654
18692
  );
18655
18693
  const allAncestries = ancestryDataRef.current || [];
@@ -19037,7 +19075,7 @@ function XViewScene({
19037
19075
  );
19038
19076
  if (!ancestryObject || !ancestryObject.abstraction_tree) return;
19039
19077
  const { ancestryGroup, nodeObjects, renderer, renderedAncestries } = stateRef.current;
19040
- const allParentNodes = Object.values(parentDataRef.current || {}).flatMap(
19078
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
19041
19079
  (f) => f.nodes
19042
19080
  );
19043
19081
  let fullTree = buildFullAncestryTree(
@@ -19125,7 +19163,7 @@ function XViewScene({
19125
19163
  const allAncestries = ancestryDataRef.current || [];
19126
19164
  const fullTree = buildFullAncestryTree(
19127
19165
  ancestry.tree,
19128
- Object.values(parentDataRef.current || {}).flatMap((f) => f.nodes),
19166
+ Object.values(parentDataRef.current).flatMap((f) => f.nodes),
19129
19167
  allAncestries
19130
19168
  );
19131
19169
  if (action === "open") {
@@ -19307,7 +19345,7 @@ function XViewScene({
19307
19345
  return null;
19308
19346
  const fullTree = buildFullAncestryTree(
19309
19347
  readingMode.ancestry.tree,
19310
- Object.values(parentDataRef.current || {}).flatMap((f) => f.nodes),
19348
+ Object.values(parentDataRef.current).flatMap((f) => f.nodes),
19311
19349
  ancestryDataRef.current
19312
19350
  );
19313
19351
  const findNodeInTree = (tree, targetId) => {
@@ -19372,7 +19410,7 @@ function XViewScene({
19372
19410
  }
19373
19411
  const fullTree = buildFullAncestryTree(
19374
19412
  readingMode.ancestry.tree,
19375
- Object.values(parentDataRef.current || {}).flatMap((f) => f.nodes),
19413
+ Object.values(parentDataRef.current).flatMap((f) => f.nodes),
19376
19414
  ancestryDataRef.current
19377
19415
  );
19378
19416
  let currentPtr = fullTree;
@@ -19469,7 +19507,7 @@ function XViewScene({
19469
19507
  let targetObj = ancestry;
19470
19508
  let targetTree = ancestry.tree;
19471
19509
  if (branchStack.length > 0) {
19472
- const allNodes = Object.values(parentDataRef.current || {}).flatMap(
19510
+ const allNodes = Object.values(parentDataRef.current).flatMap(
19473
19511
  (f) => f.nodes
19474
19512
  );
19475
19513
  const fullTree = buildFullAncestryTree(
@@ -19626,7 +19664,7 @@ function XViewScene({
19626
19664
  stateRef.current.maxAncestryRenderIndex = 0;
19627
19665
  const initialSections = /* @__PURE__ */ new Set(["preamble", 0]);
19628
19666
  await handleRenderAncestry(ancestryObject, initialSections);
19629
- const allParentNodes = Object.values(parentDataRef.current || {}).flatMap(
19667
+ const allParentNodes = Object.values(parentDataRef.current).flatMap(
19630
19668
  (fileData) => fileData.nodes
19631
19669
  );
19632
19670
  const allAncestries = ancestryDataRef.current || [];
@@ -20091,7 +20129,7 @@ function XViewScene({
20091
20129
  }, []);
20092
20130
  const allAvailableNodes = useMemo14(() => {
20093
20131
  if (!parentDataRef.current) return [];
20094
- return Object.values(parentDataRef.current || {}).flatMap(
20132
+ return Object.values(parentDataRef.current).flatMap(
20095
20133
  (fileData) => fileData.nodes || []
20096
20134
  );
20097
20135
  }, [sceneVersion, isInitialized]);
@@ -20180,7 +20218,7 @@ function XViewScene({
20180
20218
  if (!parentDataRef.current) {
20181
20219
  return [];
20182
20220
  }
20183
- const allTypes = Object.values(parentDataRef.current || {}).flatMap(
20221
+ const allTypes = Object.values(parentDataRef.current).flatMap(
20184
20222
  (fileData) => fileData.nodes.flatMap((node) => {
20185
20223
  if (Array.isArray(node.type)) return node.type;
20186
20224
  return [node.type];
@@ -20220,24 +20258,19 @@ function XViewScene({
20220
20258
  setSceneVersion((v) => v + 1);
20221
20259
  const isView = ((_a2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _a2.toLowerCase()) === "view";
20222
20260
  if (!isView || !save_view_data || !sceneSaveUrl) return;
20223
- const sceneFileData = {
20224
- ...sceneDataRef.current,
20225
- nodes: sceneDataRef.current.nodes || [],
20226
- links: sceneDataRef.current.links || [],
20227
- quest_counter: sceneDataRef.current.quest_counter || 1
20228
- };
20229
20261
  const questFileData = {
20230
20262
  nodes: ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || sceneDataRef.current.quest_nodes || [],
20231
20263
  links: ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || sceneDataRef.current.quest_links || [],
20232
20264
  milestones: newMilestones
20233
20265
  };
20234
20266
  try {
20235
- await save_view_data(sceneSaveUrl, sceneFileData);
20236
- const questSaveUrl = sceneSaveUrl.replace(
20237
- "x_view_scenes",
20238
- "x_view_quests"
20239
- );
20240
- await save_view_data(questSaveUrl, questFileData);
20267
+ if (upsert_milestone_firestore) {
20268
+ await upsert_milestone_firestore(
20269
+ ownerId,
20270
+ sceneConfigId,
20271
+ newMilestones
20272
+ );
20273
+ }
20241
20274
  } catch (error) {
20242
20275
  console.error("Erro ao salvar milestones:", error);
20243
20276
  }
@@ -20245,7 +20278,7 @@ function XViewScene({
20245
20278
  [sceneConfigId, sceneSaveUrl, save_view_data, viewParams == null ? void 0 : viewParams.type]
20246
20279
  );
20247
20280
  const handleSaveCurrentView = useCallback7(async () => {
20248
- var _a2, _b2, _c2;
20281
+ var _a2;
20249
20282
  const { nodeObjects, allLinks } = stateRef.current;
20250
20283
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
20251
20284
  console.warn(
@@ -20254,7 +20287,7 @@ function XViewScene({
20254
20287
  return;
20255
20288
  }
20256
20289
  if (!save_view_data) return;
20257
- const currentNodes = Object.values(nodeObjects || {}).map((mesh) => {
20290
+ const currentNodes = Object.values(nodeObjects).map((mesh) => {
20258
20291
  const {
20259
20292
  _baseEmissiveIntensity,
20260
20293
  labelObject,
@@ -20275,23 +20308,10 @@ function XViewScene({
20275
20308
  const sceneFileData = {
20276
20309
  ...sceneDataRef.current,
20277
20310
  nodes: currentNodes,
20278
- links: currentLinks,
20279
- quest_counter: sceneDataRef.current.quest_counter || 1
20280
- };
20281
- const questFileData = {
20282
- nodes: isView ? ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || [] : sceneDataRef.current.quest_nodes || [],
20283
- links: isView ? ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || [] : sceneDataRef.current.quest_links || [],
20284
- milestones: sceneDataRef.current.milestones || []
20311
+ links: currentLinks
20285
20312
  };
20286
20313
  try {
20287
20314
  await save_view_data(sceneSaveUrl, sceneFileData);
20288
- if (isView) {
20289
- const questSaveUrl = sceneSaveUrl.replace(
20290
- "x_view_scenes",
20291
- "x_view_quests"
20292
- );
20293
- await save_view_data(questSaveUrl, questFileData);
20294
- }
20295
20315
  } catch (error) {
20296
20316
  console.error("Erro na chamada de save_view_data:", error);
20297
20317
  }
@@ -20416,7 +20436,7 @@ function XViewScene({
20416
20436
  } = stateRef.current;
20417
20437
  if (graphGroup) {
20418
20438
  let ghostPosition = sceneControls.target.clone();
20419
- const existingNodes = Object.values(nodeObjects || {});
20439
+ const existingNodes = Object.values(nodeObjects);
20420
20440
  let isOccupied = true;
20421
20441
  let radius = 18;
20422
20442
  let angle = 0;
@@ -21154,7 +21174,7 @@ async function save_view_data_logic(db_services, filename, data) {
21154
21174
  }
21155
21175
  }
21156
21176
  async function get_scene_view_data_logic(db_services, scene_config, owner_id, type, session, focusNodeId = null, focusAncestryId = null) {
21157
- var _a, _b;
21177
+ var _a, _b, _c;
21158
21178
  try {
21159
21179
  if (!session || !session.user) {
21160
21180
  return { success: false, error: "Usu\xE1rio n\xE3o autenticado." };
@@ -21196,54 +21216,70 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
21196
21216
  const parentResponsesPromises = parentDbObjects.map(
21197
21217
  (db_info) => db_services.get_file(`x_view_dbs/${db_info.owner_id}/${db_info.db_id}`)
21198
21218
  );
21199
- const parentResponsesResults = await Promise.allSettled(
21200
- parentResponsesPromises
21201
- );
21202
- const parentData = {};
21203
- let currentQuestNodes = [];
21204
- let currentQuestLinks = [];
21205
- let currentMilestones = [];
21206
21219
  if (isView) {
21207
- const firestoreQuestsRes = await db_services.get_view_quests_and_links(
21208
- owner_id,
21209
- scene_config
21210
- );
21211
- const isMigrated = sceneData.quests_migrated === true;
21212
- if (firestoreQuestsRes.success && (firestoreQuestsRes.data.nodes.length > 0 || isMigrated)) {
21213
- currentQuestNodes = firestoreQuestsRes.data.nodes;
21214
- currentQuestLinks = firestoreQuestsRes.data.links;
21215
- currentMilestones = sceneData.milestones || [];
21216
- } else {
21217
- console.log(`Iniciando Lazy Migration para View ${scene_config}...`);
21218
- const legacyQuestsRes = await db_services.get_file(
21219
- `x_view_quests/${owner_id}/${scene_config}`
21220
- );
21221
- if (legacyQuestsRes.success && legacyQuestsRes.data) {
21222
- const legacyData = legacyQuestsRes.data;
21223
- currentQuestNodes = legacyData.nodes || [];
21224
- currentQuestLinks = legacyData.links || [];
21225
- currentMilestones = legacyData.milestones || [];
21226
- sceneData.milestones = currentMilestones;
21227
- await db_services.migrate_legacy_quests(
21220
+ const questFetchWithMigrationPromise = (async () => {
21221
+ try {
21222
+ const questsResponse = await db_services.get_view_quests_firestore(
21228
21223
  owner_id,
21229
- scene_config,
21230
- legacyData
21224
+ scene_config
21231
21225
  );
21226
+ if (questsResponse.success && questsResponse.hasQuests) {
21227
+ return {
21228
+ success: true,
21229
+ data: questsResponse.data,
21230
+ quest_counter: questsResponse.quest_counter
21231
+ };
21232
+ } else {
21233
+ const legacyResponse = await db_services.get_file(
21234
+ `x_view_quests/${owner_id}/${scene_config}`
21235
+ );
21236
+ if (legacyResponse.success && legacyResponse.data) {
21237
+ await db_services.migrate_json_to_firestore(
21238
+ owner_id,
21239
+ scene_config,
21240
+ legacyResponse.data,
21241
+ sceneData.quest_counter
21242
+ );
21243
+ legacyResponse.quest_counter = sceneData.quest_counter || 1;
21244
+ return legacyResponse;
21245
+ }
21246
+ return legacyResponse;
21247
+ }
21248
+ } catch (err) {
21249
+ return { success: false, error: err.message };
21232
21250
  }
21233
- }
21251
+ })();
21252
+ parentResponsesPromises.push(questFetchWithMigrationPromise);
21234
21253
  }
21254
+ const parentResponsesResults = await Promise.allSettled(
21255
+ parentResponsesPromises
21256
+ );
21257
+ const parentData = {};
21258
+ let questDataObj = null;
21235
21259
  for (let i = 0; i < parentResponsesResults.length; i++) {
21236
21260
  const result = parentResponsesResults[i];
21261
+ if (isView && i === parentResponsesResults.length - 1) {
21262
+ if (result.status === "fulfilled" && ((_a = result.value) == null ? void 0 : _a.success)) {
21263
+ questDataObj = result.value.data;
21264
+ if (result.value.quest_counter !== void 0) {
21265
+ sceneData.quest_counter = result.value.quest_counter;
21266
+ }
21267
+ }
21268
+ continue;
21269
+ }
21237
21270
  const db_info = parentDbObjects[i];
21238
- if (result.status === "fulfilled" && ((_a = result.value) == null ? void 0 : _a.success)) {
21271
+ if (result.status === "fulfilled" && ((_b = result.value) == null ? void 0 : _b.success)) {
21239
21272
  parentData[db_info.db_id] = result.value.data;
21240
21273
  } else {
21241
21274
  console.warn(
21242
21275
  `Arquivo pai n\xE3o carregado: ${db_info.db_id}. Pode ter sido deletado.`,
21243
- result.status === "rejected" ? result.reason : (_b = result.value) == null ? void 0 : _b.error
21276
+ result.status === "rejected" ? result.reason : (_c = result.value) == null ? void 0 : _c.error
21244
21277
  );
21245
21278
  }
21246
21279
  }
21280
+ const currentQuestNodes = (questDataObj == null ? void 0 : questDataObj.nodes) || [];
21281
+ const currentQuestLinks = (questDataObj == null ? void 0 : questDataObj.links) || [];
21282
+ const currentMilestones = (questDataObj == null ? void 0 : questDataObj.milestones) || [];
21247
21283
  if (isView) {
21248
21284
  parentData[scene_config] = {
21249
21285
  dataset_name: "Quests Internas (View)",
@@ -21251,8 +21287,8 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
21251
21287
  links: currentQuestLinks
21252
21288
  };
21253
21289
  }
21254
- const allNodes = Object.values(parentData).flatMap((db) => (db == null ? void 0 : db.nodes) || []);
21255
- const allLinks = Object.values(parentData).flatMap((db) => (db == null ? void 0 : db.links) || []);
21290
+ const allNodes = Object.values(parentData).flatMap((db) => db.nodes || []);
21291
+ const allLinks = Object.values(parentData).flatMap((db) => db.links || []);
21256
21292
  const parentNodeMap = new Map(
21257
21293
  allNodes.map((node) => [String(node.id), node])
21258
21294
  );
@@ -21293,8 +21329,6 @@ async function get_scene_view_data_logic(db_services, scene_config, owner_id, ty
21293
21329
  ...sceneData,
21294
21330
  nodes: validatedNodes,
21295
21331
  links: validatedLinks,
21296
- // Re-injetando no validated para que o restante do sistema (e.g. XViewScene local state)
21297
- // continue acessando as quests sem perceber a separação física
21298
21332
  quest_nodes: currentQuestNodes,
21299
21333
  quest_links: currentQuestLinks,
21300
21334
  milestones: currentMilestones