@lv-x-software-house/x_view 1.2.5-dev.46 → 1.2.5-dev.48

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 +546 -183
  2. package/dist/index.mjs +477 -114
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -44,9 +44,9 @@ __export(index_exports, {
44
44
  module.exports = __toCommonJS(index_exports);
45
45
 
46
46
  // src/XViewScene.jsx
47
- var import_react28 = __toESM(require("react"));
47
+ var import_react29 = __toESM(require("react"));
48
48
  var import_navigation = require("next/navigation");
49
- var import_react29 = require("next-auth/react");
49
+ var import_react30 = require("next-auth/react");
50
50
  var import_crypto_js = __toESM(require("crypto-js"));
51
51
  var THREE3 = __toESM(require("three"));
52
52
  var import_OrbitControls = require("three/examples/jsm/controls/OrbitControls.js");
@@ -625,6 +625,9 @@ function XViewSidebar({
625
625
  userRole,
626
626
  viewMembers = [],
627
627
  onActivateQuestLens = () => {
628
+ },
629
+ milestones = [],
630
+ onOpenMilestoneManager = () => {
628
631
  }
629
632
  }) {
630
633
  const [query, setQuery] = (0, import_react2.useState)("");
@@ -1033,25 +1036,7 @@ function XViewSidebar({
1033
1036
  },
1034
1037
  /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "17 8 12 3 7 8" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "12", y1: "3", x2: "12", y2: "15" })),
1035
1038
  "Importar arquivo"
1036
- ), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mt-1.5 text-xs text-slate-400 text-center px-1 leading-tight" }, "Importar datasets ou ancestralidades das views."), /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-9 mt-1 flex items-center justify-center" }, importSuccessMessage && /* @__PURE__ */ import_react2.default.createElement("p", { className: "w-full text-center text-xs font-medium text-indigo-300 bg-indigo-500/10 border border-indigo-500/20 rounded-md py-1.5 px-2" }, importSuccessMessage)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Lentes"), /* @__PURE__ */ import_react2.default.createElement(
1037
- "button",
1038
- {
1039
- onClick: onActivateTimeline,
1040
- className: buttonBaseClass,
1041
- title: "Ativar a visualiza\xE7\xE3o de Timeline"
1042
- },
1043
- /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "12 6 12 12 16 14" })),
1044
- "Timeline"
1045
- ), /* @__PURE__ */ import_react2.default.createElement(
1046
- "button",
1047
- {
1048
- onClick: onOpenAncestryBoard,
1049
- className: buttonBaseClass,
1050
- title: "Abrir painel de Ancestralidades"
1051
- },
1052
- /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "2 12 12 17 22 12" })),
1053
- "Ancestry Board"
1054
- ), /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative", ref: questLensRef }, /* @__PURE__ */ import_react2.default.createElement(
1039
+ ), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mt-1.5 text-xs text-slate-400 text-center px-1 leading-tight" }, "Importar datasets ou ancestralidades das views."), /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-9 mt-1 flex items-center justify-center" }, importSuccessMessage && /* @__PURE__ */ import_react2.default.createElement("p", { className: "w-full text-center text-xs font-medium text-indigo-300 bg-indigo-500/10 border border-indigo-500/20 rounded-md py-1.5 px-2" }, importSuccessMessage)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Lentes"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative", ref: questLensRef }, /* @__PURE__ */ import_react2.default.createElement(
1055
1040
  "button",
1056
1041
  {
1057
1042
  onClick: () => setIsQuestLensMenuOpen(!isQuestLensMenuOpen),
@@ -1103,7 +1088,35 @@ function XViewSidebar({
1103
1088
  className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95"
1104
1089
  },
1105
1090
  "Aplicar Lente"
1106
- )))))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ import_react2.default.createElement(
1091
+ )))), /* @__PURE__ */ import_react2.default.createElement(
1092
+ "button",
1093
+ {
1094
+ onClick: onActivateTimeline,
1095
+ className: buttonBaseClass,
1096
+ title: "Ativar a visualiza\xE7\xE3o de Timeline"
1097
+ },
1098
+ /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "12 6 12 12 16 14" })),
1099
+ "Timeline"
1100
+ ), /* @__PURE__ */ import_react2.default.createElement(
1101
+ "button",
1102
+ {
1103
+ onClick: onOpenAncestryBoard,
1104
+ className: buttonBaseClass,
1105
+ title: "Abrir painel de Ancestralidades"
1106
+ },
1107
+ /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "2 12 12 17 22 12" })),
1108
+ "Ancestry Board"
1109
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-3" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Milestones"), /* @__PURE__ */ import_react2.default.createElement(
1110
+ "button",
1111
+ {
1112
+ onClick: onOpenMilestoneManager,
1113
+ className: buttonBaseClass,
1114
+ title: "Gerenciar os milestones deste cen\xE1rio"
1115
+ },
1116
+ /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12", cy: "12", r: "6" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "12", cy: "12", r: "2" })),
1117
+ "Gerenciar Milestones",
1118
+ milestones.length > 0 && /* @__PURE__ */ import_react2.default.createElement("span", { className: "ml-1.5 px-1.5 py-0.5 rounded-full bg-indigo-500/30 text-[10px] font-bold border border-indigo-400/30" }, milestones.length)
1119
+ ))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ import_react2.default.createElement(
1107
1120
  "button",
1108
1121
  {
1109
1122
  onClick: handleSaveClick,
@@ -2332,7 +2345,8 @@ var userActionHandlers = {
2332
2345
  nodes: sceneDataRef.current.nodes,
2333
2346
  links: sceneDataRef.current.links,
2334
2347
  quest_nodes: specificParentData.nodes,
2335
- quest_links: specificParentData.links
2348
+ quest_links: specificParentData.links,
2349
+ milestones: sceneDataRef.current.milestones || []
2336
2350
  };
2337
2351
  await context.actions.save_view_data(
2338
2352
  context.sceneSaveUrl,
@@ -2590,7 +2604,8 @@ var userActionHandlers = {
2590
2604
  nodes: sceneDataRef.current.nodes,
2591
2605
  links: sceneDataRef.current.links,
2592
2606
  quest_nodes: specificParentData.nodes,
2593
- quest_links: specificParentData.links
2607
+ quest_links: specificParentData.links,
2608
+ milestones: sceneDataRef.current.milestones || []
2594
2609
  };
2595
2610
  await context.actions.save_view_data(
2596
2611
  context.sceneSaveUrl,
@@ -2746,7 +2761,8 @@ var userActionHandlers = {
2746
2761
  nodes: sceneDataRef.current.nodes,
2747
2762
  links: sceneDataRef.current.links,
2748
2763
  quest_nodes: data.nodes,
2749
- quest_links: data.links
2764
+ quest_links: data.links,
2765
+ milestones: sceneDataRef.current.milestones || []
2750
2766
  };
2751
2767
  return context.actions.save_view_data(
2752
2768
  context.sceneSaveUrl,
@@ -3360,6 +3376,7 @@ var userActionHandlers = {
3360
3376
  links: sceneDataRef.current.links,
3361
3377
  quest_nodes: specificParentData.nodes,
3362
3378
  quest_links: specificParentData.links,
3379
+ milestones: sceneDataRef.current.milestones || [],
3363
3380
  quest_counter: sceneDataRef.current.quest_counter || 1
3364
3381
  };
3365
3382
  await context.actions.save_view_data(
@@ -3432,7 +3449,8 @@ var userActionHandlers = {
3432
3449
  links: sceneDataRef.current.links,
3433
3450
  quest_nodes: specificParentData.nodes,
3434
3451
  quest_links: specificParentData.links,
3435
- quest_counter: sceneDataRef.current.quest_counter || 1
3452
+ quest_counter: sceneDataRef.current.quest_counter || 1,
3453
+ milestones: sceneDataRef.current.milestones || []
3436
3454
  };
3437
3455
  await context.actions.save_view_data(
3438
3456
  context.sceneSaveUrl,
@@ -9468,11 +9486,15 @@ function InSceneQuestForm({
9468
9486
  questCounter = 1,
9469
9487
  // NOVA PROP
9470
9488
  viewMembers = [],
9471
- session = null
9489
+ session = null,
9490
+ milestones = [],
9491
+ onUpdateMilestones = () => {
9492
+ }
9472
9493
  }) {
9473
- var _a, _b;
9494
+ var _a, _b, _c;
9474
9495
  const [name, setName] = (0, import_react17.useState)("");
9475
9496
  const [assigneeId, setAssigneeId] = (0, import_react17.useState)("");
9497
+ const [milestoneId, setMilestoneId] = (0, import_react17.useState)("");
9476
9498
  const [types, setTypes] = (0, import_react17.useState)(["quest"]);
9477
9499
  const [typeInput, setTypeInput] = (0, import_react17.useState)("");
9478
9500
  const [status, setStatus] = (0, import_react17.useState)("Backlog");
@@ -9494,6 +9516,32 @@ function InSceneQuestForm({
9494
9516
  const [customProps, setCustomProps] = (0, import_react17.useState)([]);
9495
9517
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react17.useState)(false);
9496
9518
  const propsEndRef = (0, import_react17.useRef)(null);
9519
+ const [isMilestoneDropdownOpen, setIsMilestoneDropdownOpen] = (0, import_react17.useState)(false);
9520
+ const [milestoneSearchQuery, setMilestoneSearchQuery] = (0, import_react17.useState)("");
9521
+ const activeMilestones = import_react17.default.useMemo(
9522
+ () => (milestones || []).filter((ms) => ms.status === "active"),
9523
+ [milestones]
9524
+ );
9525
+ const filteredMilestones = import_react17.default.useMemo(() => {
9526
+ if (!milestoneSearchQuery.trim()) return activeMilestones;
9527
+ return activeMilestones.filter(
9528
+ (ms) => ms.name.toLowerCase().includes(milestoneSearchQuery.toLowerCase())
9529
+ );
9530
+ }, [activeMilestones, milestoneSearchQuery]);
9531
+ const handleSelectMilestone = (id) => {
9532
+ setMilestoneId(id);
9533
+ setIsMilestoneDropdownOpen(false);
9534
+ setMilestoneSearchQuery("");
9535
+ };
9536
+ const handleCreateMilestone = async (name2) => {
9537
+ if (!name2.trim()) return;
9538
+ const newId = `ms_${Date.now()}`;
9539
+ const newMs = { id: newId, name: name2.trim(), status: "active" };
9540
+ await onUpdateMilestones([...milestones, newMs]);
9541
+ setMilestoneId(newId);
9542
+ setIsMilestoneDropdownOpen(false);
9543
+ setMilestoneSearchQuery("");
9544
+ };
9497
9545
  const standardizedName = `${viewName} - ${questCounter} - \xBB ${name || "Nova Quest"}`;
9498
9546
  const handleAddProp = () => {
9499
9547
  const newProp = createNewCustomProperty(customProps);
@@ -9561,6 +9609,7 @@ function InSceneQuestForm({
9561
9609
  timeline: initialTimeline,
9562
9610
  Date: questDate ? { ...questDate, confirmed: true } : null,
9563
9611
  recurrence_config: recurrenceConfig,
9612
+ milestone_id: milestoneId || null,
9564
9613
  ...additionalData
9565
9614
  });
9566
9615
  };
@@ -9664,6 +9713,63 @@ function InSceneQuestForm({
9664
9713
  }
9665
9714
  ),
9666
9715
  s
9716
+ )))))), /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Milestone"), /* @__PURE__ */ import_react17.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react17.default.createElement(
9717
+ "button",
9718
+ {
9719
+ type: "button",
9720
+ onClick: () => setIsMilestoneDropdownOpen(!isMilestoneDropdownOpen),
9721
+ className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
9722
+ },
9723
+ /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiTarget, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = (milestones || []).find((m) => m.id === milestoneId)) == null ? void 0 : _a.name) || "Nenhum")),
9724
+ /* @__PURE__ */ import_react17.default.createElement(
9725
+ import_fi15.FiChevronDown,
9726
+ {
9727
+ className: `text-slate-400 transition-transform duration-200 ${isMilestoneDropdownOpen ? "rotate-180" : ""}`
9728
+ }
9729
+ )
9730
+ ), isMilestoneDropdownOpen && /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement(
9731
+ "div",
9732
+ {
9733
+ className: "fixed inset-0 z-40",
9734
+ onClick: () => {
9735
+ setIsMilestoneDropdownOpen(false);
9736
+ setMilestoneSearchQuery("");
9737
+ }
9738
+ }
9739
+ ), /* @__PURE__ */ import_react17.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react17.default.createElement(
9740
+ "input",
9741
+ {
9742
+ type: "text",
9743
+ autoFocus: true,
9744
+ placeholder: "Buscar ou criar milestone...",
9745
+ value: milestoneSearchQuery,
9746
+ onChange: (e) => setMilestoneSearchQuery(e.target.value),
9747
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9748
+ onClick: (e) => e.stopPropagation()
9749
+ }
9750
+ )), /* @__PURE__ */ import_react17.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement(
9751
+ "li",
9752
+ {
9753
+ onClick: () => handleSelectMilestone(""),
9754
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!milestoneId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
9755
+ },
9756
+ "Nenhum"
9757
+ ), filteredMilestones.map((ms) => /* @__PURE__ */ import_react17.default.createElement(
9758
+ "li",
9759
+ {
9760
+ key: ms.id,
9761
+ onClick: () => handleSelectMilestone(ms.id),
9762
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${milestoneId === ms.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
9763
+ },
9764
+ ms.name
9765
+ )), milestoneSearchQuery.trim() && !filteredMilestones.some((ms) => ms.name.toLowerCase() === milestoneSearchQuery.toLowerCase().trim()) && /* @__PURE__ */ import_react17.default.createElement(
9766
+ "li",
9767
+ {
9768
+ onClick: () => handleCreateMilestone(milestoneSearchQuery),
9769
+ className: "px-3 py-3 text-sm cursor-pointer bg-indigo-600/20 text-indigo-200 hover:bg-indigo-600/40 transition-colors flex flex-col gap-0.5"
9770
+ },
9771
+ /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-[10px] uppercase font-bold opacity-60" }, "Criar Novo Milestone"),
9772
+ /* @__PURE__ */ import_react17.default.createElement("span", { className: "font-medium" }, '"', milestoneSearchQuery, '"')
9667
9773
  )))))), /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ import_react17.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react17.default.createElement(
9668
9774
  "button",
9669
9775
  {
@@ -9671,7 +9777,7 @@ function InSceneQuestForm({
9671
9777
  onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9672
9778
  className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
9673
9779
  },
9674
- /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _a.name) || ((_b = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _b.email) || "Nenhum")),
9780
+ /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_b = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _b.name) || ((_c = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _c.email) || "Nenhum")),
9675
9781
  /* @__PURE__ */ import_react17.default.createElement(
9676
9782
  import_fi15.FiChevronDown,
9677
9783
  {
@@ -10643,9 +10749,12 @@ function QuestDetailsPanel({
10643
10749
  userRole,
10644
10750
  currentDatasetName,
10645
10751
  viewMembers = [],
10646
- currentUser
10752
+ currentUser,
10753
+ milestones = [],
10754
+ onUpdateMilestones = () => {
10755
+ }
10647
10756
  }) {
10648
- var _a, _b;
10757
+ var _a, _b, _c;
10649
10758
  const initialRawTitle = (node == null ? void 0 : node.raw_title) || (((_a = node == null ? void 0 : node.name) == null ? void 0 : _a.includes(" - \xBB ")) ? node.name.split(" - \xBB ")[1] : node == null ? void 0 : node.name) || "";
10650
10759
  const [rawTitle, setRawTitle] = (0, import_react20.useState)(initialRawTitle);
10651
10760
  const prefixParts = ((node == null ? void 0 : node.name) || "").split(" - \xBB ");
@@ -10658,6 +10767,7 @@ function QuestDetailsPanel({
10658
10767
  const [assigneeId, setAssigneeId] = (0, import_react20.useState)((node == null ? void 0 : node.assignee_id) || "");
10659
10768
  const [description, setDescription] = (0, import_react20.useState)((node == null ? void 0 : node.description) ?? "");
10660
10769
  const [intensity, setIntensity] = (0, import_react20.useState)((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
10770
+ const [milestoneId, setMilestoneId] = (0, import_react20.useState)((node == null ? void 0 : node.milestone_id) || "");
10661
10771
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react20.useState)(false);
10662
10772
  const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react20.useState)(false);
10663
10773
  const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react20.useState)("");
@@ -10695,6 +10805,35 @@ function QuestDetailsPanel({
10695
10805
  minWidth: 320,
10696
10806
  maxWidth: maxPanelW
10697
10807
  });
10808
+ const [isMilestoneDropdownOpen, setIsMilestoneDropdownOpen] = (0, import_react20.useState)(false);
10809
+ const [milestoneSearchQuery, setMilestoneSearchQuery] = (0, import_react20.useState)("");
10810
+ const activeMilestones = import_react20.default.useMemo(
10811
+ () => (milestones || []).filter((ms) => ms.status === "active"),
10812
+ [milestones]
10813
+ );
10814
+ const filteredMilestones = import_react20.default.useMemo(() => {
10815
+ const search = milestoneSearchQuery.toLowerCase().trim();
10816
+ if (!search) return activeMilestones;
10817
+ return activeMilestones.filter(
10818
+ (ms) => ms.name.toLowerCase().includes(search)
10819
+ );
10820
+ }, [activeMilestones, milestoneSearchQuery]);
10821
+ const handleSelectMilestone = (id) => {
10822
+ setMilestoneId(id);
10823
+ setIsMilestoneDropdownOpen(false);
10824
+ setMilestoneSearchQuery("");
10825
+ setHasUnsavedChanges(true);
10826
+ };
10827
+ const handleCreateMilestone = async (name) => {
10828
+ if (!name.trim()) return;
10829
+ const newId = `ms_${Date.now()}`;
10830
+ const newMs = { id: newId, name: name.trim(), status: "active" };
10831
+ await onUpdateMilestones([...milestones, newMs]);
10832
+ setMilestoneId(newId);
10833
+ setIsMilestoneDropdownOpen(false);
10834
+ setMilestoneSearchQuery("");
10835
+ setHasUnsavedChanges(true);
10836
+ };
10698
10837
  (0, import_react20.useEffect)(() => {
10699
10838
  setWidth(isReadMode ? 700 : 440);
10700
10839
  }, [isReadMode, setWidth]);
@@ -10720,6 +10859,7 @@ function QuestDetailsPanel({
10720
10859
  setReturnAssigneeId("");
10721
10860
  setDescription((node == null ? void 0 : node.description) ?? "");
10722
10861
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
10862
+ setMilestoneId((node == null ? void 0 : node.milestone_id) || "");
10723
10863
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
10724
10864
  setCustomProps(extractCustomPropsFromNode(node || {}));
10725
10865
  setReviewStack((node == null ? void 0 : node.reviewStack) || []);
@@ -10732,6 +10872,7 @@ function QuestDetailsPanel({
10732
10872
  custom_date: "",
10733
10873
  next_assignee_id: ""
10734
10874
  });
10875
+ setMilestoneId((node == null ? void 0 : node.milestone_id) || "");
10735
10876
  setHasUnsavedChanges(false);
10736
10877
  }
10737
10878
  }, [node]);
@@ -11030,7 +11171,8 @@ ${noteText}` : noteText : entry.notes,
11030
11171
  reviewStack: currentReviewStack,
11031
11172
  timeline: currentTimeline,
11032
11173
  ...extrasObj,
11033
- version_node: node.version_node
11174
+ version_node: node.version_node,
11175
+ milestone_id: milestoneId || null
11034
11176
  };
11035
11177
  await onSave(dataToSave, keepOpen);
11036
11178
  onDataUpdate(dataToSave);
@@ -11232,7 +11374,51 @@ ${noteText}` : noteText : entry.notes,
11232
11374
  maxLength: "10",
11233
11375
  className: "flex-1 bg-slate-800/70 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-1 focus:ring-indigo-400/60"
11234
11376
  }
11235
- ))))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react20.default.createElement(
11377
+ ))))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Milestone"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react20.default.createElement(
11378
+ "button",
11379
+ {
11380
+ type: "button",
11381
+ onClick: () => canEdit && setIsMilestoneDropdownOpen(!isMilestoneDropdownOpen),
11382
+ disabled: !canEdit,
11383
+ className: `w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 transition-colors flex items-center justify-between ${canEdit ? "hover:border-white/20 focus:ring-2 focus:ring-indigo-400/60 cursor-pointer" : "cursor-default opacity-80"}`
11384
+ },
11385
+ /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiTarget, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_b = (milestones || []).find((m) => m.id === milestoneId)) == null ? void 0 : _b.name) || "Nenhum")),
11386
+ canEdit && /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isMilestoneDropdownOpen ? "rotate-180" : ""}` })
11387
+ ), isMilestoneDropdownOpen && canEdit && /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsMilestoneDropdownOpen(false) }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react20.default.createElement(
11388
+ "input",
11389
+ {
11390
+ type: "text",
11391
+ autoFocus: true,
11392
+ placeholder: "Buscar ou criar milestone...",
11393
+ value: milestoneSearchQuery,
11394
+ onChange: (e) => setMilestoneSearchQuery(e.target.value),
11395
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
11396
+ onClick: (e) => e.stopPropagation()
11397
+ }
11398
+ )), /* @__PURE__ */ import_react20.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement(
11399
+ "li",
11400
+ {
11401
+ onClick: () => handleSelectMilestone(""),
11402
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!milestoneId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
11403
+ },
11404
+ "Nenhum"
11405
+ ), filteredMilestones.map((ms) => /* @__PURE__ */ import_react20.default.createElement(
11406
+ "li",
11407
+ {
11408
+ key: ms.id,
11409
+ onClick: () => handleSelectMilestone(ms.id),
11410
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${milestoneId === ms.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
11411
+ },
11412
+ ms.name
11413
+ )), milestoneSearchQuery.trim() && !filteredMilestones.some((ms) => ms.name.toLowerCase() === milestoneSearchQuery.toLowerCase().trim()) && /* @__PURE__ */ import_react20.default.createElement(
11414
+ "li",
11415
+ {
11416
+ onClick: () => handleCreateMilestone(milestoneSearchQuery),
11417
+ className: "px-3 py-3 text-sm cursor-pointer bg-indigo-600/20 text-indigo-200 hover:bg-indigo-600/40 transition-colors flex flex-col gap-0.5"
11418
+ },
11419
+ /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-[10px] uppercase font-bold opacity-60" }, "Criar Novo Milestone"),
11420
+ /* @__PURE__ */ import_react20.default.createElement("span", { className: "font-medium" }, '"', milestoneSearchQuery, '"')
11421
+ )))))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react20.default.createElement(
11236
11422
  "button",
11237
11423
  {
11238
11424
  type: "button",
@@ -11418,7 +11604,7 @@ ${noteText}` : noteText : entry.notes,
11418
11604
  onClick: () => setIsReturnDropdownOpen(!isReturnDropdownOpen),
11419
11605
  className: "w-full bg-slate-800/80 p-2 text-xs rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-1 focus:ring-purple-400/50 transition-colors flex items-center justify-between"
11420
11606
  },
11421
- /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiUser, { className: "text-slate-400", size: 12 }), /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_b = viewMembers.find((m) => String(m.id) === String(returnAssigneeId))) == null ? void 0 : _b.name) || "Selecionar respons\xE1vel...")),
11607
+ /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiUser, { className: "text-slate-400", size: 12 }), /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_c = viewMembers.find((m) => String(m.id) === String(returnAssigneeId))) == null ? void 0 : _c.name) || "Selecionar respons\xE1vel...")),
11422
11608
  /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isReturnDropdownOpen ? "rotate-180" : ""}`, size: 14 })
11423
11609
  ), isReturnDropdownOpen && /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
11424
11610
  setIsReturnDropdownOpen(false);
@@ -13005,6 +13191,116 @@ function AncestryBoard({
13005
13191
  );
13006
13192
  }
13007
13193
 
13194
+ // src/components/MilestoneManager.jsx
13195
+ var import_react28 = __toESM(require("react"));
13196
+ var import_fi22 = require("react-icons/fi");
13197
+ function MilestoneManager({
13198
+ isOpen,
13199
+ onClose,
13200
+ milestones = [],
13201
+ onUpdateMilestones = () => {
13202
+ }
13203
+ }) {
13204
+ const [newMilestoneName, setNewMilestoneName] = (0, import_react28.useState)("");
13205
+ const handleAddMilestone = () => {
13206
+ if (!newMilestoneName.trim()) return;
13207
+ const newMilestone = {
13208
+ id: `ms_${Date.now()}`,
13209
+ name: newMilestoneName.trim(),
13210
+ status: "active"
13211
+ };
13212
+ onUpdateMilestones([...milestones, newMilestone]);
13213
+ setNewMilestoneName("");
13214
+ };
13215
+ const handleToggleMilestoneStatus = (id) => {
13216
+ const updated = milestones.map(
13217
+ (ms) => ms.id === id ? { ...ms, status: ms.status === "active" ? "inactive" : "active" } : ms
13218
+ );
13219
+ onUpdateMilestones(updated);
13220
+ };
13221
+ const handleRemoveMilestone = (id) => {
13222
+ var _a;
13223
+ if (!confirm(`Deseja remover o milestone "${(_a = milestones.find((m) => m.id === id)) == null ? void 0 : _a.name}"?`)) return;
13224
+ const updated = milestones.filter((ms) => ms.id !== id);
13225
+ onUpdateMilestones(updated);
13226
+ };
13227
+ if (!isOpen) return null;
13228
+ const swallow = (e) => e.stopPropagation();
13229
+ return /* @__PURE__ */ import_react28.default.createElement(
13230
+ "div",
13231
+ {
13232
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/40 backdrop-blur-sm animate-in fade-in duration-300",
13233
+ onClick: onClose
13234
+ },
13235
+ /* @__PURE__ */ import_react28.default.createElement(
13236
+ "div",
13237
+ {
13238
+ className: "w-full max-w-lg bg-slate-900/90 backdrop-blur-xl border border-white/10 rounded-2xl shadow-2xl flex flex-col max-h-[85vh] animate-in zoom-in-95 duration-300",
13239
+ onClick: swallow
13240
+ },
13241
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "px-6 py-4 border-b border-white/10 flex items-center justify-between" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "p-2 rounded-lg bg-indigo-500/20 text-indigo-400" }, /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiTarget, { size: 20 })), /* @__PURE__ */ import_react28.default.createElement("div", null, /* @__PURE__ */ import_react28.default.createElement("h2", { className: "text-lg font-semibold text-slate-100" }, "Milestones"), /* @__PURE__ */ import_react28.default.createElement("p", { className: "text-xs text-slate-400" }, "Gerencie os marcos do seu cen\xE1rio"))), /* @__PURE__ */ import_react28.default.createElement(
13242
+ "button",
13243
+ {
13244
+ onClick: onClose,
13245
+ className: "p-2 rounded-full hover:bg-white/10 text-slate-400 hover:text-white transition-colors"
13246
+ },
13247
+ /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiX, { size: 20 })
13248
+ )),
13249
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "px-6 py-5 bg-white/5 border-b border-white/5" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ import_react28.default.createElement(
13250
+ "input",
13251
+ {
13252
+ type: "text",
13253
+ value: newMilestoneName,
13254
+ onChange: (e) => setNewMilestoneName(e.target.value),
13255
+ onKeyDown: (e) => e.key === "Enter" && handleAddMilestone(),
13256
+ placeholder: "Adicionar novo milestone...",
13257
+ className: "flex-1 bg-slate-800/50 border border-white/10 rounded-xl px-4 py-2.5 text-sm text-slate-100 placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-indigo-500/40 focus:border-indigo-500/50 transition-all shadow-inner"
13258
+ }
13259
+ ), /* @__PURE__ */ import_react28.default.createElement(
13260
+ "button",
13261
+ {
13262
+ onClick: handleAddMilestone,
13263
+ disabled: !newMilestoneName.trim(),
13264
+ className: "px-4 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 disabled:opacity-30 disabled:cursor-not-allowed text-white text-sm font-medium transition-all flex items-center gap-2 shadow-lg shadow-indigo-500/20 active:scale-95"
13265
+ },
13266
+ /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiPlus, { size: 18 }),
13267
+ "Adicionar"
13268
+ ))),
13269
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex-1 overflow-y-auto px-6 py-4 custom-scrollbar" }, milestones.length > 0 ? /* @__PURE__ */ import_react28.default.createElement("div", { className: "space-y-3" }, milestones.map((ms) => /* @__PURE__ */ import_react28.default.createElement(
13270
+ "div",
13271
+ {
13272
+ key: ms.id,
13273
+ className: `group flex items-center justify-between p-4 rounded-xl border transition-all duration-200 ${ms.status === "active" ? "bg-slate-800/40 border-indigo-500/20 hover:border-indigo-500/40" : "bg-slate-900/40 border-white/5 opacity-60 grayscale-[0.3]"}`
13274
+ },
13275
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex items-center gap-3 min-w-0" }, /* @__PURE__ */ import_react28.default.createElement(
13276
+ "div",
13277
+ {
13278
+ className: `w-2 h-2 rounded-full shrink-0 ${ms.status === "active" ? "bg-indigo-400 shadow-[0_0_8px_rgba(129,140,248,0.6)]" : "bg-slate-600"}`
13279
+ }
13280
+ ), /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex flex-col min-w-0" }, /* @__PURE__ */ import_react28.default.createElement("span", { className: `text-sm font-medium truncate ${ms.status === "active" ? "text-slate-100" : "text-slate-400 line-through"}` }, ms.name), /* @__PURE__ */ import_react28.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider" }, ms.status === "active" ? "Ativo" : "Inativo"))),
13281
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react28.default.createElement(
13282
+ "button",
13283
+ {
13284
+ onClick: () => handleToggleMilestoneStatus(ms.id),
13285
+ className: `p-2 rounded-lg transition-all ${ms.status === "active" ? "hover:bg-slate-700 text-slate-400" : "hover:bg-indigo-500/20 text-indigo-400"}`,
13286
+ title: ms.status === "active" ? "Inativar" : "Ativar"
13287
+ },
13288
+ ms.status === "active" ? /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiPauseCircle, { size: 18 }) : /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiPlayCircle, { size: 18 })
13289
+ ), /* @__PURE__ */ import_react28.default.createElement(
13290
+ "button",
13291
+ {
13292
+ onClick: () => handleRemoveMilestone(ms.id),
13293
+ className: "p-2 rounded-lg hover:bg-red-500/20 text-slate-500 hover:text-red-400 transition-all",
13294
+ title: "Remover"
13295
+ },
13296
+ /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiTrash2, { size: 18 })
13297
+ ))
13298
+ ))) : /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex flex-col items-center justify-center py-12 text-center" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "p-4 rounded-full bg-slate-800/30 text-slate-600 mb-4" }, /* @__PURE__ */ import_react28.default.createElement(import_fi22.FiTarget, { size: 40 })), /* @__PURE__ */ import_react28.default.createElement("h3", { className: "text-slate-400 font-medium" }, "Nenhum milestone definido"), /* @__PURE__ */ import_react28.default.createElement("p", { className: "text-xs text-slate-600 max-w-[200px] mt-1 italic" }, "Defina marcos importantes para orientar o progresso do projeto."))),
13299
+ /* @__PURE__ */ import_react28.default.createElement("div", { className: "px-6 py-4 border-t border-white/5 bg-slate-900/50 flex items-center justify-between rounded-b-2xl" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex gap-4" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react28.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Total:"), /* @__PURE__ */ import_react28.default.createElement("span", { className: "text-xs font-bold text-slate-300" }, milestones.length)), /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex items-center gap-1.5" }, /* @__PURE__ */ import_react28.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-widest" }, "Ativos:"), /* @__PURE__ */ import_react28.default.createElement("span", { className: "text-xs font-bold text-indigo-400" }, milestones.filter((m) => m.status === "active").length))))
13300
+ )
13301
+ );
13302
+ }
13303
+
13008
13304
  // src/XViewScene.jsx
13009
13305
  var import_short_uuid2 = __toESM(require("short-uuid"));
13010
13306
  var secretKey = process.env.NEXT_PUBLIC_CRYPTO_KEY;
@@ -13086,13 +13382,13 @@ function XViewScene({
13086
13382
  check_user_permission,
13087
13383
  get_view_members
13088
13384
  }) {
13089
- var _a, _b, _c, _d, _e, _f, _g, _h;
13090
- const { data: session, status } = (0, import_react29.useSession)();
13385
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
13386
+ const { data: session, status } = (0, import_react30.useSession)();
13091
13387
  const router = (0, import_navigation.useRouter)();
13092
13388
  const searchParams = (0, import_navigation.useSearchParams)();
13093
13389
  const focusNodeId = searchParams == null ? void 0 : searchParams.get("focus");
13094
13390
  const focusAncestryId = searchParams == null ? void 0 : searchParams.get("ancestry");
13095
- const viewParams = (0, import_react28.useMemo)(() => {
13391
+ const viewParams = (0, import_react29.useMemo)(() => {
13096
13392
  if (encryptedConfig) {
13097
13393
  const data = decryptData(encryptedConfig);
13098
13394
  if (data) {
@@ -13101,7 +13397,7 @@ function XViewScene({
13101
13397
  }
13102
13398
  return null;
13103
13399
  }, [encryptedConfig, session]);
13104
- (0, import_react28.useEffect)(() => {
13400
+ (0, import_react29.useEffect)(() => {
13105
13401
  async function verifyPermission() {
13106
13402
  if (!viewParams || !session || !check_user_permission) return;
13107
13403
  const { id, type, owner_id } = viewParams;
@@ -13138,65 +13434,65 @@ function XViewScene({
13138
13434
  setIsLoading(false);
13139
13435
  }
13140
13436
  }, [viewParams, session, status, check_user_permission]);
13141
- const sceneConfigId = (0, import_react28.useMemo)(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
13142
- const ownerId = (0, import_react28.useMemo)(() => (viewParams == null ? void 0 : viewParams.owner_id) || null, [viewParams]);
13143
- const dbSaveUrl = (0, import_react28.useMemo)(() => {
13437
+ const sceneConfigId = (0, import_react29.useMemo)(() => (viewParams == null ? void 0 : viewParams.id) || null, [viewParams]);
13438
+ const ownerId = (0, import_react29.useMemo)(() => (viewParams == null ? void 0 : viewParams.owner_id) || null, [viewParams]);
13439
+ const dbSaveUrl = (0, import_react29.useMemo)(() => {
13144
13440
  if (ownerId && sceneConfigId) {
13145
13441
  return `x_view_dbs/${ownerId}/${sceneConfigId}`;
13146
13442
  }
13147
13443
  return null;
13148
13444
  }, [ownerId, sceneConfigId]);
13149
- const sceneSaveUrl = (0, import_react28.useMemo)(() => {
13445
+ const sceneSaveUrl = (0, import_react29.useMemo)(() => {
13150
13446
  if (ownerId && sceneConfigId) {
13151
13447
  return `x_view_scenes/${ownerId}/${sceneConfigId}`;
13152
13448
  }
13153
13449
  return null;
13154
13450
  }, [ownerId, sceneConfigId]);
13155
- const ancestry_save_url = (0, import_react28.useMemo)(() => {
13451
+ const ancestry_save_url = (0, import_react29.useMemo)(() => {
13156
13452
  if (ownerId && sceneConfigId) {
13157
13453
  return `x_view_ancestry/${ownerId}/${sceneConfigId}`;
13158
13454
  }
13159
13455
  return null;
13160
13456
  }, [ownerId, sceneConfigId]);
13161
- const sceneDataRef = (0, import_react28.useRef)(null);
13162
- const parentDataRef = (0, import_react28.useRef)(null);
13163
- const ancestryDataRef = (0, import_react28.useRef)(null);
13164
- const [isLoading, setIsLoading] = (0, import_react28.useState)(true);
13165
- const [permissionStatus, setPermissionStatus] = (0, import_react28.useState)("loading");
13166
- const [userPermissionRole, setUserPermissionRole] = (0, import_react28.useState)(null);
13167
- const [viewMembers, setViewMembers] = (0, import_react28.useState)([]);
13168
- const [isInitialized, setIsInitialized] = (0, import_react28.useState)(false);
13169
- const [sceneVersion, setSceneVersion] = (0, import_react28.useState)(0);
13170
- const [contextMenu, setContextMenu] = (0, import_react28.useState)({
13457
+ const sceneDataRef = (0, import_react29.useRef)(null);
13458
+ const parentDataRef = (0, import_react29.useRef)(null);
13459
+ const ancestryDataRef = (0, import_react29.useRef)(null);
13460
+ const [isLoading, setIsLoading] = (0, import_react29.useState)(true);
13461
+ const [permissionStatus, setPermissionStatus] = (0, import_react29.useState)("loading");
13462
+ const [userPermissionRole, setUserPermissionRole] = (0, import_react29.useState)(null);
13463
+ const [viewMembers, setViewMembers] = (0, import_react29.useState)([]);
13464
+ const [isInitialized, setIsInitialized] = (0, import_react29.useState)(false);
13465
+ const [sceneVersion, setSceneVersion] = (0, import_react29.useState)(0);
13466
+ const [contextMenu, setContextMenu] = (0, import_react29.useState)({
13171
13467
  visible: false,
13172
13468
  x: 0,
13173
13469
  y: 0,
13174
13470
  nodeData: null
13175
13471
  });
13176
- const [multiContextMenu, setMultiContextMenu] = (0, import_react28.useState)({
13472
+ const [multiContextMenu, setMultiContextMenu] = (0, import_react29.useState)({
13177
13473
  visible: false,
13178
13474
  x: 0,
13179
13475
  y: 0,
13180
13476
  nodeIds: null
13181
13477
  });
13182
- const [relationshipMenu, setRelationshipMenu] = (0, import_react28.useState)({
13478
+ const [relationshipMenu, setRelationshipMenu] = (0, import_react29.useState)({
13183
13479
  visible: false,
13184
13480
  x: 0,
13185
13481
  y: 0,
13186
13482
  linkObject: null
13187
13483
  });
13188
- const [creationMode, setCreationMode] = (0, import_react28.useState)({
13484
+ const [creationMode, setCreationMode] = (0, import_react29.useState)({
13189
13485
  isActive: false,
13190
13486
  sourceNodeData: null
13191
13487
  });
13192
- const [versionMode, setVersionMode] = (0, import_react28.useState)({
13488
+ const [versionMode, setVersionMode] = (0, import_react29.useState)({
13193
13489
  isActive: false,
13194
13490
  sourceNodeData: null
13195
13491
  });
13196
- const [questMode, setQuestMode] = (0, import_react28.useState)({ isActive: false });
13197
- const [hasFocusedInitial, setHasFocusedInitial] = (0, import_react28.useState)(false);
13198
- const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = (0, import_react28.useState)(false);
13199
- const [ancestryMode, setAncestryMode] = (0, import_react28.useState)({
13492
+ const [questMode, setQuestMode] = (0, import_react29.useState)({ isActive: false });
13493
+ const [hasFocusedInitial, setHasFocusedInitial] = (0, import_react29.useState)(false);
13494
+ const [hasOpenedInitialAncestry, setHasOpenedInitialAncestry] = (0, import_react29.useState)(false);
13495
+ const [ancestryMode, setAncestryMode] = (0, import_react29.useState)({
13200
13496
  isActive: false,
13201
13497
  tree: null,
13202
13498
  selectedParentId: null,
@@ -13207,41 +13503,42 @@ function XViewScene({
13207
13503
  ancestryDescriptionSections: [],
13208
13504
  isAddingNodes: false
13209
13505
  });
13210
- const [readingMode, setReadingMode] = (0, import_react28.useState)({
13506
+ const [readingMode, setReadingMode] = (0, import_react29.useState)({
13211
13507
  isActive: false,
13212
13508
  ancestry: null,
13213
13509
  branchStack: [],
13214
13510
  autoAbstraction: false
13215
13511
  });
13216
- const [formPosition, setFormPosition] = (0, import_react28.useState)({
13512
+ const [formPosition, setFormPosition] = (0, import_react29.useState)({
13217
13513
  left: 16,
13218
13514
  top: 16,
13219
13515
  opacity: 0
13220
13516
  });
13221
- const [detailsNode, setDetailsNode] = (0, import_react28.useState)(null);
13222
- const [detailsLink, setDetailsLink] = (0, import_react28.useState)(null);
13223
- const [ancestryLinkDetails, setAncestryLinkDetails] = (0, import_react28.useState)(null);
13224
- const [imageViewer, setImageViewer] = (0, import_react28.useState)({
13517
+ const [detailsNode, setDetailsNode] = (0, import_react29.useState)(null);
13518
+ const [detailsLink, setDetailsLink] = (0, import_react29.useState)(null);
13519
+ const [ancestryLinkDetails, setAncestryLinkDetails] = (0, import_react29.useState)(null);
13520
+ const [imageViewer, setImageViewer] = (0, import_react29.useState)({
13225
13521
  visible: false,
13226
13522
  images: [],
13227
13523
  startIndex: 0
13228
13524
  });
13229
- const [editingAncestryRel, setEditingAncestryRel] = (0, import_react28.useState)({
13525
+ const [editingAncestryRel, setEditingAncestryRel] = (0, import_react29.useState)({
13230
13526
  visible: false,
13231
13527
  data: null,
13232
13528
  path: null
13233
13529
  });
13234
- const [isImportModalOpen, setIsImportModalOpen] = (0, import_react28.useState)(false);
13235
- const [importSuccessMessage, setImportSuccessMessage] = (0, import_react28.useState)("");
13236
- const [invalidTargetError, setInvalidTargetError] = (0, import_react28.useState)(null);
13237
- const [highlightedNodeId, setHighlightedNodeId] = (0, import_react28.useState)(null);
13238
- const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react28.useState)(false);
13239
- const [ancestryBoardData, setAncestryBoardData] = (0, import_react28.useState)([]);
13240
- const [isSidebarOpen, setIsSidebarOpen] = (0, import_react28.useState)(false);
13241
- const mountRef = (0, import_react28.useRef)(null);
13242
- const tooltipRef = (0, import_react28.useRef)(null);
13243
- const formRef = (0, import_react28.useRef)(null);
13244
- const stateRef = (0, import_react28.useRef)({
13530
+ const [isImportModalOpen, setIsImportModalOpen] = (0, import_react29.useState)(false);
13531
+ const [importSuccessMessage, setImportSuccessMessage] = (0, import_react29.useState)("");
13532
+ const [invalidTargetError, setInvalidTargetError] = (0, import_react29.useState)(null);
13533
+ const [highlightedNodeId, setHighlightedNodeId] = (0, import_react29.useState)(null);
13534
+ const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react29.useState)(false);
13535
+ const [ancestryBoardData, setAncestryBoardData] = (0, import_react29.useState)([]);
13536
+ const [isSidebarOpen, setIsSidebarOpen] = (0, import_react29.useState)(false);
13537
+ const [isMilestoneManagerOpen, setIsMilestoneManagerOpen] = (0, import_react29.useState)(false);
13538
+ const mountRef = (0, import_react29.useRef)(null);
13539
+ const tooltipRef = (0, import_react29.useRef)(null);
13540
+ const formRef = (0, import_react29.useRef)(null);
13541
+ const stateRef = (0, import_react29.useRef)({
13245
13542
  readMode: {
13246
13543
  currentMaxIndex: 0,
13247
13544
  progressMap: {}
@@ -13305,10 +13602,10 @@ function XViewScene({
13305
13602
  minWidth: 320,
13306
13603
  maxWidth: maxReadPanelW
13307
13604
  });
13308
- (0, import_react28.useEffect)(() => {
13605
+ (0, import_react29.useEffect)(() => {
13309
13606
  stateRef.current.ancestry = ancestryMode;
13310
13607
  }, [ancestryMode]);
13311
- (0, import_react28.useEffect)(() => {
13608
+ (0, import_react29.useEffect)(() => {
13312
13609
  var _a2;
13313
13610
  if (!isInitialized) return;
13314
13611
  const map = /* @__PURE__ */ new Map();
@@ -13331,10 +13628,10 @@ function XViewScene({
13331
13628
  }
13332
13629
  stateRef.current.nodeIdToParentFileMap = map;
13333
13630
  }, [isInitialized, sceneVersion]);
13334
- const handleNavigateBack = (0, import_react28.useCallback)(() => {
13631
+ const handleNavigateBack = (0, import_react29.useCallback)(() => {
13335
13632
  router.push("/dashboard/scenes");
13336
13633
  }, [router]);
13337
- const handleConfirmImport = (0, import_react28.useCallback)(
13634
+ const handleConfirmImport = (0, import_react29.useCallback)(
13338
13635
  async (importPayload) => {
13339
13636
  var _a2, _b2;
13340
13637
  let files = [];
@@ -13462,7 +13759,7 @@ function XViewScene({
13462
13759
  const handleOpenImageViewer = (images, startIndex) => {
13463
13760
  setImageViewer({ visible: true, images, startIndex });
13464
13761
  };
13465
- const tweenToTarget = (0, import_react28.useCallback)(
13762
+ const tweenToTarget = (0, import_react29.useCallback)(
13466
13763
  (target, zoomFactor = 1, forcedDirection = null) => {
13467
13764
  const { camera, controls, tweenGroup } = stateRef.current;
13468
13765
  if (!camera || !controls || !tweenGroup) return;
@@ -13488,7 +13785,7 @@ function XViewScene({
13488
13785
  if (!t || typeof t.closest !== "function") return false;
13489
13786
  return !!t.closest(".ui-overlay");
13490
13787
  };
13491
- const buildFullAncestryTree = (0, import_react28.useCallback)(
13788
+ const buildFullAncestryTree = (0, import_react29.useCallback)(
13492
13789
  (idTree, nodes, ancestries = []) => {
13493
13790
  if (!idTree) return null;
13494
13791
  const nodeMap = new Map(nodes.map((n) => [String(n.id), n]));
@@ -13573,7 +13870,7 @@ function XViewScene({
13573
13870
  },
13574
13871
  []
13575
13872
  );
13576
- const handleActivateTimeline = (0, import_react28.useCallback)(() => {
13873
+ const handleActivateTimeline = (0, import_react29.useCallback)(() => {
13577
13874
  const { nodeObjects, tweenGroup, timelineIntervalsGroup } = stateRef.current;
13578
13875
  if (!nodeObjects || !tweenGroup || !timelineIntervalsGroup) return;
13579
13876
  while (timelineIntervalsGroup.children.length > 0) {
@@ -13733,7 +14030,7 @@ function XViewScene({
13733
14030
  }
13734
14031
  });
13735
14032
  }, []);
13736
- const handleVersionTimeline = (0, import_react28.useCallback)((sourceMesh, versionMeshes) => {
14033
+ const handleVersionTimeline = (0, import_react29.useCallback)((sourceMesh, versionMeshes) => {
13737
14034
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
13738
14035
  if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
13739
14036
  return;
@@ -13870,7 +14167,7 @@ function XViewScene({
13870
14167
  }
13871
14168
  });
13872
14169
  }, []);
13873
- (0, import_react28.useEffect)(() => {
14170
+ (0, import_react29.useEffect)(() => {
13874
14171
  async function fetchAllData(configPath, ownerId2) {
13875
14172
  var _a2, _b2;
13876
14173
  if (!get_scene_view_data) {
@@ -13915,20 +14212,23 @@ function XViewScene({
13915
14212
  sceneResponse.data.scene.nodes = [];
13916
14213
  sceneResponse.data.scene.links = [];
13917
14214
  }
13918
- sceneDataRef.current = sceneResponse.data.scene;
14215
+ sceneDataRef.current = {
14216
+ ...sceneResponse.data.scene,
14217
+ milestones: sceneResponse.data.scene.milestones || []
14218
+ };
13919
14219
  parentDataRef.current = sceneResponse.data.parent;
13920
14220
  ancestryDataRef.current = sceneResponse.data.ancestry;
13921
14221
  console.log(
13922
14222
  "Console de sceneResponse.data.scene:",
13923
- sceneResponse.data.scene
14223
+ sceneDataRef.current
13924
14224
  );
13925
14225
  console.log(
13926
14226
  "Console de sceneResponse.data.parent:",
13927
- sceneResponse.data.parent
14227
+ parentDataRef.current
13928
14228
  );
13929
14229
  console.log(
13930
14230
  "Console de sceneResponse.data.ancestry:",
13931
- sceneResponse.data.ancestry
14231
+ ancestryDataRef.current
13932
14232
  );
13933
14233
  setIsInitialized(true);
13934
14234
  } else {
@@ -13972,12 +14272,12 @@ function XViewScene({
13972
14272
  focusNodeId,
13973
14273
  focusAncestryId
13974
14274
  ]);
13975
- const isNodeInView = (0, import_react28.useCallback)((id) => {
14275
+ const isNodeInView = (0, import_react29.useCallback)((id) => {
13976
14276
  const key = String(id);
13977
14277
  const objs = stateRef.current.nodeObjects || {};
13978
14278
  return !!objs[key];
13979
14279
  }, []);
13980
- const addOrUpdateNodeMesh = (0, import_react28.useCallback)(
14280
+ const addOrUpdateNodeMesh = (0, import_react29.useCallback)(
13981
14281
  (nodeData, position, suppressVersionUpdate = false) => {
13982
14282
  const {
13983
14283
  graphGroup,
@@ -14017,7 +14317,7 @@ function XViewScene({
14017
14317
  },
14018
14318
  []
14019
14319
  );
14020
- const handleActivateQuestLens = (0, import_react28.useCallback)(({ assigneeId, status: status2 }) => {
14320
+ const handleActivateQuestLens = (0, import_react29.useCallback)(({ assigneeId, status: status2 }) => {
14021
14321
  var _a2;
14022
14322
  const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
14023
14323
  if (!nodeObjects || !tweenGroup || !graphGroup) return;
@@ -14230,7 +14530,7 @@ function XViewScene({
14230
14530
  camTween.start();
14231
14531
  }
14232
14532
  }, [addOrUpdateNodeMesh]);
14233
- (0, import_react28.useEffect)(() => {
14533
+ (0, import_react29.useEffect)(() => {
14234
14534
  if (!isInitialized || !sceneDataRef.current) return;
14235
14535
  const currentMount = mountRef.current;
14236
14536
  if (!currentMount) return;
@@ -14986,7 +15286,7 @@ function XViewScene({
14986
15286
  get_scene_view_data,
14987
15287
  save_view_data
14988
15288
  ]);
14989
- const handleGhostNodeImageChange = (0, import_react28.useCallback)((useImage, imageUrl) => {
15289
+ const handleGhostNodeImageChange = (0, import_react29.useCallback)((useImage, imageUrl) => {
14990
15290
  const {
14991
15291
  node: ghostNode,
14992
15292
  line: ghostLine,
@@ -15034,7 +15334,7 @@ function XViewScene({
15034
15334
  aura: newGhostNode.getObjectByName("aura")
15035
15335
  };
15036
15336
  }, []);
15037
- const handleGhostNodeIntensityChange = (0, import_react28.useCallback)((newIntensity) => {
15337
+ const handleGhostNodeIntensityChange = (0, import_react29.useCallback)((newIntensity) => {
15038
15338
  const { node: ghostNode, aura: ghostAura } = stateRef.current.ghostElements;
15039
15339
  if (!ghostNode) return;
15040
15340
  const adjustedIntensity = newIntensity + MIN_VISIBILITY_INTENSITY;
@@ -15055,7 +15355,7 @@ function XViewScene({
15055
15355
  ghostAura.material.opacity = Math.min(0.8, newIntensity * 0.15);
15056
15356
  }
15057
15357
  }, []);
15058
- const handleDetailNodeIntensityChange = (0, import_react28.useCallback)(
15358
+ const handleDetailNodeIntensityChange = (0, import_react29.useCallback)(
15059
15359
  (nodeId, newIntensity) => {
15060
15360
  const mesh = stateRef.current.nodeObjects[String(nodeId)];
15061
15361
  if (!mesh) return;
@@ -15211,7 +15511,7 @@ function XViewScene({
15211
15511
  mountRef.current.style.cursor = "default";
15212
15512
  }
15213
15513
  };
15214
- const handleAncestryTreeUpdate = (0, import_react28.useCallback)((newTree, extraData = null) => {
15514
+ const handleAncestryTreeUpdate = (0, import_react29.useCallback)((newTree, extraData = null) => {
15215
15515
  setAncestryMode((prev) => {
15216
15516
  const prevTreeStr = JSON.stringify(prev.tree);
15217
15517
  const newTreeStr = JSON.stringify(newTree);
@@ -15234,7 +15534,7 @@ function XViewScene({
15234
15534
  };
15235
15535
  });
15236
15536
  }, []);
15237
- const actionHandlerContext = (0, import_react28.useMemo)(
15537
+ const actionHandlerContext = (0, import_react29.useMemo)(
15238
15538
  () => {
15239
15539
  var _a2;
15240
15540
  return {
@@ -15295,7 +15595,7 @@ function XViewScene({
15295
15595
  const handleStartVersioning = (nodeData) => {
15296
15596
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
15297
15597
  };
15298
- const handleStartQuestQuick = (0, import_react28.useCallback)((questNode) => {
15598
+ const handleStartQuestQuick = (0, import_react29.useCallback)((questNode) => {
15299
15599
  var _a2, _b2, _c2;
15300
15600
  if (!questNode || !actionHandlerContext) return;
15301
15601
  const {
@@ -15324,7 +15624,7 @@ function XViewScene({
15324
15624
  }
15325
15625
  setDetailsNode(updatedNode);
15326
15626
  }, [session, actionHandlerContext]);
15327
- const handleFinishQuestQuick = (0, import_react28.useCallback)((questNode) => {
15627
+ const handleFinishQuestQuick = (0, import_react29.useCallback)((questNode) => {
15328
15628
  var _a2, _b2, _c2, _d2;
15329
15629
  if (!questNode || !actionHandlerContext) return;
15330
15630
  const {
@@ -15398,7 +15698,7 @@ function XViewScene({
15398
15698
  setDetailsNode(null);
15399
15699
  }
15400
15700
  }, [session, actionHandlerContext, viewMembers, detailsNode]);
15401
- const handleCancelQuest = (0, import_react28.useCallback)(() => {
15701
+ const handleCancelQuest = (0, import_react29.useCallback)(() => {
15402
15702
  const { graphGroup, ghostElements } = stateRef.current;
15403
15703
  if (ghostElements.node && graphGroup) {
15404
15704
  if (ghostElements.node.userData.labelObject) {
@@ -15451,7 +15751,8 @@ function XViewScene({
15451
15751
  links: sceneDataRef2.current.links,
15452
15752
  quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
15453
15753
  quest_links: graphDataRef.current[sceneConfigId2].links,
15454
- quest_counter: currentCounter + 1
15754
+ quest_counter: currentCounter + 1,
15755
+ milestones: sceneDataRef2.current.milestones || []
15455
15756
  };
15456
15757
  try {
15457
15758
  await actions.save_view_data(sceneSaveUrl2, sceneFileData);
@@ -15550,7 +15851,7 @@ function XViewScene({
15550
15851
  }
15551
15852
  userActionHandlers.handleCancelConnection(context);
15552
15853
  };
15553
- const handleClearAncestryVisuals = (0, import_react28.useCallback)((ancestryId) => {
15854
+ const handleClearAncestryVisuals = (0, import_react29.useCallback)((ancestryId) => {
15554
15855
  const { renderedAncestries, ancestryGroup } = stateRef.current;
15555
15856
  const renderIndex = renderedAncestries.findIndex(
15556
15857
  (a) => String(a.id) === String(ancestryId)
@@ -15568,7 +15869,7 @@ function XViewScene({
15568
15869
  );
15569
15870
  }
15570
15871
  }, []);
15571
- const handleRenderAncestry = (0, import_react28.useCallback)(
15872
+ const handleRenderAncestry = (0, import_react29.useCallback)(
15572
15873
  async (ancestryObject, allowedSectionIds = null, activeSectionIdForFocus = null, baseRotation = 0, forceReprocess = true) => {
15573
15874
  setContextMenu(
15574
15875
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16062,7 +16363,7 @@ function XViewScene({
16062
16363
  ancestryMode.isActive
16063
16364
  ]
16064
16365
  );
16065
- const handleRenderAbstractionTree = (0, import_react28.useCallback)(
16366
+ const handleRenderAbstractionTree = (0, import_react29.useCallback)(
16066
16367
  (ancestryObject, targetNodeId = null) => {
16067
16368
  setContextMenu(
16068
16369
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16150,7 +16451,7 @@ function XViewScene({
16150
16451
  handleClearAncestryVisuals
16151
16452
  ]
16152
16453
  );
16153
- const handleReadModeBranchNav = (0, import_react28.useCallback)(
16454
+ const handleReadModeBranchNav = (0, import_react29.useCallback)(
16154
16455
  (nodeId, action, direction = "right") => {
16155
16456
  const { ancestry, branchStack } = readingMode;
16156
16457
  if (!ancestry || !ancestry.tree) return;
@@ -16328,13 +16629,13 @@ function XViewScene({
16328
16629
  },
16329
16630
  [readingMode, handleRenderAncestry, buildFullAncestryTree, tweenToTarget]
16330
16631
  );
16331
- const handleReadModeHighlight = (0, import_react28.useCallback)((nodeId) => {
16632
+ const handleReadModeHighlight = (0, import_react29.useCallback)((nodeId) => {
16332
16633
  if (stateRef.current.highlightedNodeId !== nodeId) {
16333
16634
  stateRef.current.highlightedNodeId = nodeId;
16334
16635
  }
16335
16636
  setHighlightedNodeId(nodeId);
16336
16637
  }, []);
16337
- const activeNodeBranches = (0, import_react28.useMemo)(() => {
16638
+ const activeNodeBranches = (0, import_react29.useMemo)(() => {
16338
16639
  if (!highlightedNodeId || !readingMode.ancestry || !readingMode.ancestry.tree)
16339
16640
  return null;
16340
16641
  const fullTree = buildFullAncestryTree(
@@ -16378,7 +16679,7 @@ function XViewScene({
16378
16679
  readingMode.branchStack,
16379
16680
  ancestryDataRef.current
16380
16681
  ]);
16381
- const backNavigationInfo = (0, import_react28.useMemo)(() => {
16682
+ const backNavigationInfo = (0, import_react29.useMemo)(() => {
16382
16683
  const { branchStack } = readingMode;
16383
16684
  if (!branchStack || branchStack.length === 0) return null;
16384
16685
  const lastStep = branchStack[branchStack.length - 1];
@@ -16389,7 +16690,7 @@ function XViewScene({
16389
16690
  name: "Voltar para anterior"
16390
16691
  };
16391
16692
  }, [readingMode.branchStack]);
16392
- const getReadModeDisplayContext = (0, import_react28.useMemo)(() => {
16693
+ const getReadModeDisplayContext = (0, import_react29.useMemo)(() => {
16393
16694
  const { ancestry, branchStack } = readingMode;
16394
16695
  if (!ancestry) return null;
16395
16696
  if (branchStack.length === 0) {
@@ -16432,7 +16733,7 @@ function XViewScene({
16432
16733
  customProperties: branchProps
16433
16734
  };
16434
16735
  }, [readingMode, buildFullAncestryTree, ancestryDataRef.current]);
16435
- const readModeAbstractionTree = (0, import_react28.useMemo)(() => {
16736
+ const readModeAbstractionTree = (0, import_react29.useMemo)(() => {
16436
16737
  if (!readingMode.isActive || !readingMode.ancestry || !readingMode.ancestry.abstraction_tree) {
16437
16738
  return null;
16438
16739
  }
@@ -16451,7 +16752,7 @@ function XViewScene({
16451
16752
  buildFullAncestryTree,
16452
16753
  sceneVersion
16453
16754
  ]);
16454
- const handleStartReadingAncestry = (0, import_react28.useCallback)(
16755
+ const handleStartReadingAncestry = (0, import_react29.useCallback)(
16455
16756
  async (ancestryObject, renderMode = "full") => {
16456
16757
  setContextMenu(
16457
16758
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16494,7 +16795,7 @@ function XViewScene({
16494
16795
  },
16495
16796
  [handleRenderAncestry, handleRenderAbstractionTree]
16496
16797
  );
16497
- const handleReadModeSectionChange = (0, import_react28.useCallback)(
16798
+ const handleReadModeSectionChange = (0, import_react29.useCallback)(
16498
16799
  (activeSectionId) => {
16499
16800
  const { ancestry, branchStack } = readingMode;
16500
16801
  if (!ancestry || !readingMode.isActive) return;
@@ -16579,10 +16880,10 @@ function XViewScene({
16579
16880
  ancestryDataRef.current
16580
16881
  ]
16581
16882
  );
16582
- const handleCloseReadMode = (0, import_react28.useCallback)(() => {
16883
+ const handleCloseReadMode = (0, import_react29.useCallback)(() => {
16583
16884
  setReadingMode({ isActive: false, ancestry: null, branchStack: [] });
16584
16885
  }, []);
16585
- const handleAncestrySectionChange = (0, import_react28.useCallback)(
16886
+ const handleAncestrySectionChange = (0, import_react29.useCallback)(
16586
16887
  (activeSectionId, ancestryOverride = null, rotation = 0) => {
16587
16888
  var _a2, _b2;
16588
16889
  const currentMode = stateRef.current.ancestry;
@@ -16644,7 +16945,7 @@ function XViewScene({
16644
16945
  },
16645
16946
  [handleRenderAncestry]
16646
16947
  );
16647
- const handleEditAncestry = (0, import_react28.useCallback)(
16948
+ const handleEditAncestry = (0, import_react29.useCallback)(
16648
16949
  async (ancestryObject) => {
16649
16950
  setContextMenu(
16650
16951
  (prev) => prev.visible ? { ...prev, visible: false } : prev
@@ -16699,7 +17000,7 @@ function XViewScene({
16699
17000
  const handleSelectAncestryParent = (nodeId) => {
16700
17001
  setAncestryMode((prev) => ({ ...prev, selectedParentId: nodeId }));
16701
17002
  };
16702
- const handleRemoveFromAncestry = (0, import_react28.useCallback)((pathToRemove) => {
17003
+ const handleRemoveFromAncestry = (0, import_react29.useCallback)((pathToRemove) => {
16703
17004
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) {
16704
17005
  console.warn("Tentativa de remover a raiz ou caminho inv\xE1lido.");
16705
17006
  return;
@@ -16724,7 +17025,7 @@ function XViewScene({
16724
17025
  return { ...prev, tree: newTree };
16725
17026
  });
16726
17027
  }, []);
16727
- const handleSaveAncestry = (0, import_react28.useCallback)(
17028
+ const handleSaveAncestry = (0, import_react29.useCallback)(
16728
17029
  async (ancestryName, ancestryDescription, ancestrySections, keepOpen = false, treeOverride = null, ancestryCustomProps = {}) => {
16729
17030
  const treeToUse = treeOverride || ancestryMode.tree;
16730
17031
  const { isEditMode, currentAncestryId } = ancestryMode;
@@ -16960,7 +17261,7 @@ function XViewScene({
16960
17261
  });
16961
17262
  setEditingAncestryRel({ visible: false, data: null, path: null });
16962
17263
  };
16963
- const handleDeleteAncestry = (0, import_react28.useCallback)(
17264
+ const handleDeleteAncestry = (0, import_react29.useCallback)(
16964
17265
  async (ancestryIdToDelete) => {
16965
17266
  if (!ancestryIdToDelete) {
16966
17267
  alert("ID da ancestralidade n\xE3o encontrado.");
@@ -17042,10 +17343,10 @@ function XViewScene({
17042
17343
  },
17043
17344
  [save_view_data, delete_file_action]
17044
17345
  );
17045
- const handleOpenAncestryBoard = (0, import_react28.useCallback)(() => {
17346
+ const handleOpenAncestryBoard = (0, import_react29.useCallback)(() => {
17046
17347
  setIsAncestryBoardOpen(true);
17047
17348
  }, []);
17048
- const handleSelectAncestryFromBoard = (0, import_react28.useCallback)(
17349
+ const handleSelectAncestryFromBoard = (0, import_react29.useCallback)(
17049
17350
  (ancestry) => {
17050
17351
  setIsAncestryBoardOpen(false);
17051
17352
  setIsSidebarOpen(false);
@@ -17053,7 +17354,7 @@ function XViewScene({
17053
17354
  },
17054
17355
  [handleStartReadingAncestry]
17055
17356
  );
17056
- const handleSaveAncestryBoard = (0, import_react28.useCallback)(
17357
+ const handleSaveAncestryBoard = (0, import_react29.useCallback)(
17057
17358
  async (groups) => {
17058
17359
  if (!sceneConfigId || !viewParams || !session) return;
17059
17360
  const sceneType = (viewParams.type || "").toLowerCase().includes("database") ? "database" : "view";
@@ -17067,7 +17368,7 @@ function XViewScene({
17067
17368
  },
17068
17369
  [sceneConfigId, viewParams, session, save_ancestry_board_action, ownerId]
17069
17370
  );
17070
- const existingNodeTypes = (0, import_react28.useMemo)(() => {
17371
+ const existingNodeTypes = (0, import_react29.useMemo)(() => {
17071
17372
  if (!parentDataRef.current) {
17072
17373
  return [];
17073
17374
  }
@@ -17079,7 +17380,7 @@ function XViewScene({
17079
17380
  ).filter((t) => Boolean(t) && String(t).toLowerCase() !== "quest");
17080
17381
  return [...new Set(allTypes)];
17081
17382
  }, [parentDataRef.current, sceneVersion]);
17082
- const searchableDbNodes = (0, import_react28.useMemo)(() => {
17383
+ const searchableDbNodes = (0, import_react29.useMemo)(() => {
17083
17384
  if (!parentDataRef.current) {
17084
17385
  return [];
17085
17386
  }
@@ -17094,7 +17395,7 @@ function XViewScene({
17094
17395
  return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
17095
17396
  });
17096
17397
  }, [parentDataRef.current, sceneVersion]);
17097
- const handleAddExistingNode = (0, import_react28.useCallback)(
17398
+ const handleAddExistingNode = (0, import_react29.useCallback)(
17098
17399
  (nodeId) => {
17099
17400
  return userActionHandlers.handleAddExistingNodeById(
17100
17401
  actionHandlerContext,
@@ -17103,7 +17404,53 @@ function XViewScene({
17103
17404
  },
17104
17405
  [actionHandlerContext]
17105
17406
  );
17106
- const handleSaveCurrentView = (0, import_react28.useCallback)(async () => {
17407
+ const handleUpdateMilestones = (0, import_react29.useCallback)(
17408
+ async (newMilestones) => {
17409
+ var _a2, _b2, _c2;
17410
+ if (!sceneDataRef.current) return;
17411
+ sceneDataRef.current.milestones = newMilestones;
17412
+ setSceneVersion((v) => v + 1);
17413
+ const isView = ((_a2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _a2.toLowerCase()) === "view";
17414
+ if (!isView || !save_view_data || !sceneSaveUrl) return;
17415
+ const { nodeObjects, allLinks } = stateRef.current;
17416
+ const currentNodes = Object.values(nodeObjects || {}).map((mesh) => {
17417
+ const {
17418
+ _baseEmissiveIntensity,
17419
+ labelObject,
17420
+ labelOffset,
17421
+ timelineIntervalBar,
17422
+ timelineEndLabel,
17423
+ ...serializableNodeData
17424
+ } = mesh.userData;
17425
+ return serializableNodeData;
17426
+ });
17427
+ const currentLinks = (allLinks || []).map((line) => {
17428
+ const { sourceNode, targetNode, ...serializableLinkData } = line.userData;
17429
+ return serializableLinkData;
17430
+ });
17431
+ const sceneFileData = {
17432
+ parent_dbs: sceneDataRef.current.parent_dbs,
17433
+ nodes: currentNodes,
17434
+ links: currentLinks,
17435
+ quest_nodes: ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || sceneDataRef.current.quest_nodes || [],
17436
+ quest_links: ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || sceneDataRef.current.quest_links || [],
17437
+ quest_counter: sceneDataRef.current.quest_counter || 1,
17438
+ milestones: newMilestones
17439
+ };
17440
+ try {
17441
+ await save_view_data(sceneSaveUrl, sceneFileData);
17442
+ } catch (error) {
17443
+ console.error("Erro ao salvar milestones:", error);
17444
+ }
17445
+ },
17446
+ [
17447
+ sceneConfigId,
17448
+ sceneSaveUrl,
17449
+ save_view_data,
17450
+ viewParams == null ? void 0 : viewParams.type
17451
+ ]
17452
+ );
17453
+ const handleSaveCurrentView = (0, import_react29.useCallback)(async () => {
17107
17454
  var _a2, _b2, _c2;
17108
17455
  const { nodeObjects, allLinks } = stateRef.current;
17109
17456
  if (!nodeObjects || !allLinks || !sceneSaveUrl || !parentDataRef.current) {
@@ -17137,7 +17484,8 @@ function XViewScene({
17137
17484
  links: currentLinks,
17138
17485
  quest_nodes: isView ? ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || [] : sceneDataRef.current.quest_nodes || [],
17139
17486
  quest_links: isView ? ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || [] : sceneDataRef.current.quest_links || [],
17140
- quest_counter: sceneDataRef.current.quest_counter || 1
17487
+ quest_counter: sceneDataRef.current.quest_counter || 1,
17488
+ milestones: sceneDataRef.current.milestones || []
17141
17489
  };
17142
17490
  try {
17143
17491
  await save_view_data(sceneSaveUrl, sceneFileData);
@@ -17145,16 +17493,16 @@ function XViewScene({
17145
17493
  console.error("Erro na chamada de save_view_data:", error);
17146
17494
  }
17147
17495
  }, [sceneSaveUrl, save_view_data, sceneConfigId, viewParams == null ? void 0 : viewParams.type]);
17148
- const allAvailableNodes = (0, import_react28.useMemo)(() => {
17496
+ const allAvailableNodes = (0, import_react29.useMemo)(() => {
17149
17497
  if (!parentDataRef.current) return [];
17150
17498
  return Object.values(parentDataRef.current).flatMap(
17151
17499
  (fileData) => fileData.nodes || []
17152
17500
  );
17153
17501
  }, [sceneVersion, isInitialized]);
17154
- const allAvailableAncestries = (0, import_react28.useMemo)(() => {
17502
+ const allAvailableAncestries = (0, import_react29.useMemo)(() => {
17155
17503
  return ancestryDataRef.current || [];
17156
17504
  }, [sceneVersion, isInitialized]);
17157
- const handleOpenReference = (0, import_react28.useCallback)(
17505
+ const handleOpenReference = (0, import_react29.useCallback)(
17158
17506
  (referenceData) => {
17159
17507
  const { type, id } = referenceData;
17160
17508
  if (type === "node") {
@@ -17193,13 +17541,13 @@ function XViewScene({
17193
17541
  tweenToTarget
17194
17542
  ]
17195
17543
  );
17196
- const handleToggleAncestryAddMode = (0, import_react28.useCallback)(() => {
17544
+ const handleToggleAncestryAddMode = (0, import_react29.useCallback)(() => {
17197
17545
  setAncestryMode((prev) => ({
17198
17546
  ...prev,
17199
17547
  isAddingNodes: !prev.isAddingNodes
17200
17548
  }));
17201
17549
  }, []);
17202
- const handleFocusNode = (0, import_react28.useCallback)(
17550
+ const handleFocusNode = (0, import_react29.useCallback)(
17203
17551
  (nodeData) => {
17204
17552
  if (!nodeData) return;
17205
17553
  const nodeMesh = stateRef.current.nodeObjects[String(nodeData.id)];
@@ -17209,7 +17557,7 @@ function XViewScene({
17209
17557
  },
17210
17558
  [tweenToTarget]
17211
17559
  );
17212
- const availableDatasets = (0, import_react28.useMemo)(() => {
17560
+ const availableDatasets = (0, import_react29.useMemo)(() => {
17213
17561
  if (!sceneDataRef.current || !parentDataRef.current) return [];
17214
17562
  return sceneDataRef.current.parent_dbs.map((db) => {
17215
17563
  var _a2;
@@ -17223,7 +17571,7 @@ function XViewScene({
17223
17571
  String(creationMode.sourceNodeData.id)
17224
17572
  )) == null ? void 0 : _b.parentFileId : null;
17225
17573
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
17226
- (0, import_react28.useEffect)(() => {
17574
+ (0, import_react29.useEffect)(() => {
17227
17575
  if (isInitialized && focusNodeId && !hasFocusedInitial) {
17228
17576
  const nodeObjects = stateRef.current.nodeObjects || {};
17229
17577
  const targetMesh = nodeObjects[String(focusNodeId)];
@@ -17246,7 +17594,7 @@ function XViewScene({
17246
17594
  hasFocusedInitial,
17247
17595
  tweenToTarget
17248
17596
  ]);
17249
- (0, import_react28.useEffect)(() => {
17597
+ (0, import_react29.useEffect)(() => {
17250
17598
  if (isInitialized && focusAncestryId && !hasOpenedInitialAncestry) {
17251
17599
  const ancestries = ancestryDataRef.current || [];
17252
17600
  const targetAncestry = ancestries.find(
@@ -17271,7 +17619,7 @@ function XViewScene({
17271
17619
  hasOpenedInitialAncestry,
17272
17620
  handleStartReadingAncestry
17273
17621
  ]);
17274
- (0, import_react28.useEffect)(() => {
17622
+ (0, import_react29.useEffect)(() => {
17275
17623
  function handleKeyDown(event) {
17276
17624
  var _a2, _b2, _c2;
17277
17625
  const context = actionHandlerContext;
@@ -17394,10 +17742,10 @@ function XViewScene({
17394
17742
  // <-- handleCancelQuest adicionado aqui
17395
17743
  ]);
17396
17744
  if (isLoading || status === "loading" || permissionStatus === "loading") {
17397
- return /* @__PURE__ */ import_react28.default.createElement(LoadingScreen, null);
17745
+ return /* @__PURE__ */ import_react29.default.createElement(LoadingScreen, null);
17398
17746
  }
17399
17747
  if (permissionStatus === "denied") {
17400
- return /* @__PURE__ */ import_react28.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ import_react28.default.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ import_react28.default.createElement(
17748
+ return /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-screen w-full bg-slate-950 text-white" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "bg-slate-900/50 p-8 rounded-2xl border border-slate-800 shadow-2xl text-center max-w-md" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "mb-4 text-red-500" }, /* @__PURE__ */ import_react29.default.createElement(
17401
17749
  "svg",
17402
17750
  {
17403
17751
  xmlns: "http://www.w3.org/2000/svg",
@@ -17407,7 +17755,7 @@ function XViewScene({
17407
17755
  stroke: "currentColor",
17408
17756
  className: "w-16 h-16 mx-auto"
17409
17757
  },
17410
- /* @__PURE__ */ import_react28.default.createElement(
17758
+ /* @__PURE__ */ import_react29.default.createElement(
17411
17759
  "path",
17412
17760
  {
17413
17761
  strokeLinecap: "round",
@@ -17415,13 +17763,13 @@ function XViewScene({
17415
17763
  d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
17416
17764
  }
17417
17765
  )
17418
- )), /* @__PURE__ */ import_react28.default.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ import_react28.default.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ import_react28.default.createElement(
17766
+ )), /* @__PURE__ */ import_react29.default.createElement("h2", { className: "text-2xl font-bold mb-2" }, "Acesso Negado"), /* @__PURE__ */ import_react29.default.createElement("p", { className: "text-slate-400 mb-6" }, "Voc\xEA n\xE3o tem permiss\xE3o para acessar este conte\xFAdo. Solicite acesso ao propriet\xE1rio ou verifique se est\xE1 na conta correta."), /* @__PURE__ */ import_react29.default.createElement(
17419
17767
  "button",
17420
17768
  {
17421
17769
  onClick: () => router.push("/dashboard/scenes"),
17422
17770
  className: "flex items-center justify-center gap-2 w-full py-3 px-4 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors font-medium"
17423
17771
  },
17424
- /* @__PURE__ */ import_react28.default.createElement(
17772
+ /* @__PURE__ */ import_react29.default.createElement(
17425
17773
  "svg",
17426
17774
  {
17427
17775
  xmlns: "http://www.w3.org/2000/svg",
@@ -17431,7 +17779,7 @@ function XViewScene({
17431
17779
  stroke: "currentColor",
17432
17780
  className: "w-5 h-5"
17433
17781
  },
17434
- /* @__PURE__ */ import_react28.default.createElement(
17782
+ /* @__PURE__ */ import_react29.default.createElement(
17435
17783
  "path",
17436
17784
  {
17437
17785
  strokeLinecap: "round",
@@ -17443,7 +17791,7 @@ function XViewScene({
17443
17791
  "Voltar para Scenes"
17444
17792
  )));
17445
17793
  }
17446
- return /* @__PURE__ */ import_react28.default.createElement(
17794
+ return /* @__PURE__ */ import_react29.default.createElement(
17447
17795
  "div",
17448
17796
  {
17449
17797
  ref: mountRef,
@@ -17455,7 +17803,7 @@ function XViewScene({
17455
17803
  cursor: stateRef.current.connection.isActive || stateRef.current.relink.isActive || ancestryMode.isActive ? "crosshair" : creationMode.isActive ? "default" : "grab"
17456
17804
  }
17457
17805
  },
17458
- userPermissionRole !== "link_viewer" && /* @__PURE__ */ import_react28.default.createElement(
17806
+ userPermissionRole !== "link_viewer" && /* @__PURE__ */ import_react29.default.createElement(
17459
17807
  XViewSidebar,
17460
17808
  {
17461
17809
  dbNodes: searchableDbNodes,
@@ -17474,10 +17822,12 @@ function XViewScene({
17474
17822
  isOpen: isSidebarOpen,
17475
17823
  onToggle: setIsSidebarOpen,
17476
17824
  userRole: userPermissionRole,
17477
- viewMembers
17825
+ viewMembers,
17826
+ milestones: ((_c = sceneDataRef.current) == null ? void 0 : _c.milestones) || [],
17827
+ onOpenMilestoneManager: () => setIsMilestoneManagerOpen(true)
17478
17828
  }
17479
17829
  ),
17480
- creationMode.isActive && /* @__PURE__ */ import_react28.default.createElement(
17830
+ creationMode.isActive && /* @__PURE__ */ import_react29.default.createElement(
17481
17831
  InSceneCreationForm,
17482
17832
  {
17483
17833
  onSave: (data) => userActionHandlers.handleSaveNode(actionHandlerContext, data),
@@ -17498,8 +17848,8 @@ function XViewScene({
17498
17848
  },
17499
17849
  refEl: formRef,
17500
17850
  existingTypes: existingNodeTypes,
17501
- initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
17502
- sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
17851
+ initialColor: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.color,
17852
+ sourceTypes: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.type,
17503
17853
  onIntensityChange: handleGhostNodeIntensityChange,
17504
17854
  onUploadFile: upload_file_action,
17505
17855
  availableDatasets,
@@ -17509,7 +17859,7 @@ function XViewScene({
17509
17859
  availableAncestries: allAvailableAncestries
17510
17860
  }
17511
17861
  ),
17512
- versionMode.isActive && /* @__PURE__ */ import_react28.default.createElement(
17862
+ versionMode.isActive && /* @__PURE__ */ import_react29.default.createElement(
17513
17863
  InSceneVersionForm,
17514
17864
  {
17515
17865
  onSave: (data) => userActionHandlers.handleSaveVersionNode(actionHandlerContext, data),
@@ -17528,14 +17878,14 @@ function XViewScene({
17528
17878
  transition: "opacity 200ms ease-out"
17529
17879
  },
17530
17880
  refEl: formRef,
17531
- fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
17532
- fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
17881
+ fixedType: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.type,
17882
+ fixedColor: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.color,
17533
17883
  onUploadFile: upload_file_action,
17534
17884
  availableNodes: allAvailableNodes,
17535
17885
  availableAncestries: allAvailableAncestries
17536
17886
  }
17537
17887
  ),
17538
- questMode.isActive && /* @__PURE__ */ import_react28.default.createElement(
17888
+ questMode.isActive && /* @__PURE__ */ import_react29.default.createElement(
17539
17889
  InSceneQuestForm,
17540
17890
  {
17541
17891
  onSave: (data) => handleSaveQuestNode(actionHandlerContext, data),
@@ -17557,11 +17907,13 @@ function XViewScene({
17557
17907
  availableNodes: allAvailableNodes,
17558
17908
  availableAncestries: allAvailableAncestries,
17559
17909
  viewName: viewParams == null ? void 0 : viewParams.name,
17560
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
17561
- viewMembers
17910
+ questCounter: ((_h = sceneDataRef.current) == null ? void 0 : _h.quest_counter) || 1,
17911
+ viewMembers,
17912
+ milestones: ((_i = sceneDataRef.current) == null ? void 0 : _i.milestones) || [],
17913
+ onUpdateMilestones: handleUpdateMilestones
17562
17914
  }
17563
17915
  ),
17564
- readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react28.default.createElement(
17916
+ readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react29.default.createElement(
17565
17917
  "div",
17566
17918
  {
17567
17919
  className: `ui-overlay absolute group rounded-2xl border border-white/10 bg-slate-950/70 backdrop-blur-xl shadow-[0_20px_80px_rgba(0,0,0,0.6)] ring-1 ring-white/10 text-white overflow-hidden flex flex-col ${isReadModeResizing ? "transition-none" : "transition-all duration-300 ease-out"}`,
@@ -17574,7 +17926,7 @@ function XViewScene({
17574
17926
  maxWidth: "92vw"
17575
17927
  }
17576
17928
  },
17577
- /* @__PURE__ */ import_react28.default.createElement(
17929
+ /* @__PURE__ */ import_react29.default.createElement(
17578
17930
  "div",
17579
17931
  {
17580
17932
  onPointerDown: (e) => {
@@ -17585,7 +17937,7 @@ function XViewScene({
17585
17937
  title: "Arraste para redimensionar"
17586
17938
  }
17587
17939
  ),
17588
- /* @__PURE__ */ import_react28.default.createElement(
17940
+ /* @__PURE__ */ import_react29.default.createElement(
17589
17941
  DescriptionReadModePanel,
17590
17942
  {
17591
17943
  key: readingMode.branchStack.length > 0 ? readingMode.branchStack[readingMode.branchStack.length - 1].branchId : readingMode.ancestry.ancestry_id,
@@ -17620,7 +17972,7 @@ function XViewScene({
17620
17972
  }
17621
17973
  )
17622
17974
  ),
17623
- ancestryMode.isActive && ancestryMode.tree && /* @__PURE__ */ import_react28.default.createElement(
17975
+ ancestryMode.isActive && ancestryMode.tree && /* @__PURE__ */ import_react29.default.createElement(
17624
17976
  CreateAncestryPanel,
17625
17977
  {
17626
17978
  ancestryMode,
@@ -17656,7 +18008,7 @@ function XViewScene({
17656
18008
  onRenderAbstractionTree: (data, targetId) => handleRenderAbstractionTree(data, targetId)
17657
18009
  }
17658
18010
  ),
17659
- editingAncestryRel.visible && /* @__PURE__ */ import_react28.default.createElement(
18011
+ editingAncestryRel.visible && /* @__PURE__ */ import_react29.default.createElement(
17660
18012
  AncestryRelationshipPanel,
17661
18013
  {
17662
18014
  data: editingAncestryRel.data,
@@ -17670,7 +18022,7 @@ function XViewScene({
17670
18022
  onUploadFile: upload_file_action
17671
18023
  }
17672
18024
  ),
17673
- detailsNode && detailsNode.is_quest && /* @__PURE__ */ import_react28.default.createElement(
18025
+ detailsNode && detailsNode.is_quest && /* @__PURE__ */ import_react29.default.createElement(
17674
18026
  QuestDetailsPanel,
17675
18027
  {
17676
18028
  node: detailsNode,
@@ -17690,10 +18042,12 @@ function XViewScene({
17690
18042
  userRole: userPermissionRole,
17691
18043
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
17692
18044
  viewMembers,
17693
- currentUser: session == null ? void 0 : session.user
18045
+ currentUser: session == null ? void 0 : session.user,
18046
+ milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
18047
+ onUpdateMilestones: handleUpdateMilestones
17694
18048
  }
17695
18049
  ),
17696
- detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react28.default.createElement(
18050
+ detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react29.default.createElement(
17697
18051
  NodeDetailsPanel,
17698
18052
  {
17699
18053
  node: detailsNode,
@@ -17725,7 +18079,7 @@ function XViewScene({
17725
18079
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
17726
18080
  }
17727
18081
  ),
17728
- detailsLink && /* @__PURE__ */ import_react28.default.createElement(
18082
+ detailsLink && /* @__PURE__ */ import_react29.default.createElement(
17729
18083
  RelationshipDetailsPanel,
17730
18084
  {
17731
18085
  link: detailsLink,
@@ -17739,7 +18093,7 @@ function XViewScene({
17739
18093
  userRole: userPermissionRole
17740
18094
  }
17741
18095
  ),
17742
- ancestryLinkDetails && /* @__PURE__ */ import_react28.default.createElement(
18096
+ ancestryLinkDetails && /* @__PURE__ */ import_react29.default.createElement(
17743
18097
  AncestryLinkDetailsPanel,
17744
18098
  {
17745
18099
  data: ancestryLinkDetails,
@@ -17750,7 +18104,7 @@ function XViewScene({
17750
18104
  onUploadFile: upload_file_action
17751
18105
  }
17752
18106
  ),
17753
- /* @__PURE__ */ import_react28.default.createElement(
18107
+ /* @__PURE__ */ import_react29.default.createElement(
17754
18108
  "div",
17755
18109
  {
17756
18110
  ref: tooltipRef,
@@ -17777,7 +18131,7 @@ function XViewScene({
17777
18131
  }
17778
18132
  }
17779
18133
  ),
17780
- /* @__PURE__ */ import_react28.default.createElement(
18134
+ /* @__PURE__ */ import_react29.default.createElement(
17781
18135
  ContextMenu,
17782
18136
  {
17783
18137
  data: contextMenu,
@@ -17810,7 +18164,7 @@ function XViewScene({
17810
18164
  onFinishQuest: handleFinishQuestQuick
17811
18165
  }
17812
18166
  ),
17813
- /* @__PURE__ */ import_react28.default.createElement(
18167
+ /* @__PURE__ */ import_react29.default.createElement(
17814
18168
  MultiNodeContextMenu,
17815
18169
  {
17816
18170
  data: multiContextMenu,
@@ -17830,7 +18184,7 @@ function XViewScene({
17830
18184
  )
17831
18185
  }
17832
18186
  ),
17833
- /* @__PURE__ */ import_react28.default.createElement(
18187
+ /* @__PURE__ */ import_react29.default.createElement(
17834
18188
  RelationshipContextMenu,
17835
18189
  {
17836
18190
  data: relationshipMenu,
@@ -17848,14 +18202,14 @@ function XViewScene({
17848
18202
  onDelete: (data) => userActionHandlers.handleDeleteLink(actionHandlerContext, data)
17849
18203
  }
17850
18204
  ),
17851
- /* @__PURE__ */ import_react28.default.createElement(
18205
+ /* @__PURE__ */ import_react29.default.createElement(
17852
18206
  ImageViewer,
17853
18207
  {
17854
18208
  data: imageViewer,
17855
18209
  onClose: () => setImageViewer({ ...imageViewer, visible: false })
17856
18210
  }
17857
18211
  ),
17858
- /* @__PURE__ */ import_react28.default.createElement(
18212
+ /* @__PURE__ */ import_react29.default.createElement(
17859
18213
  AncestryBoard,
17860
18214
  {
17861
18215
  isOpen: isAncestryBoardOpen,
@@ -17868,20 +18222,29 @@ function XViewScene({
17868
18222
  userRole: userPermissionRole
17869
18223
  }
17870
18224
  ),
17871
- /* @__PURE__ */ import_react28.default.createElement(
18225
+ /* @__PURE__ */ import_react29.default.createElement(
18226
+ MilestoneManager,
18227
+ {
18228
+ isOpen: isMilestoneManagerOpen,
18229
+ onClose: () => setIsMilestoneManagerOpen(false),
18230
+ milestones: ((_k = sceneDataRef.current) == null ? void 0 : _k.milestones) || [],
18231
+ onUpdateMilestones: handleUpdateMilestones
18232
+ }
18233
+ ),
18234
+ /* @__PURE__ */ import_react29.default.createElement(
17872
18235
  ImportParentFileModal,
17873
18236
  {
17874
18237
  isOpen: isImportModalOpen,
17875
18238
  onClose: () => setIsImportModalOpen(false),
17876
18239
  onConfirm: handleConfirmImport,
17877
18240
  session,
17878
- parentDbs: ((_h = sceneDataRef.current) == null ? void 0 : _h.parent_dbs) || [],
18241
+ parentDbs: ((_l = sceneDataRef.current) == null ? void 0 : _l.parent_dbs) || [],
17879
18242
  onFetchAvailableFiles: import_parent_file_modal_get,
17880
18243
  currentViewName: viewParams == null ? void 0 : viewParams.name,
17881
18244
  currentAncestries: ancestryDataRef.current || []
17882
18245
  }
17883
18246
  ),
17884
- invalidTargetError && /* @__PURE__ */ import_react28.default.createElement(
18247
+ invalidTargetError && /* @__PURE__ */ import_react29.default.createElement(
17885
18248
  "div",
17886
18249
  {
17887
18250
  className: "ui-overlay",
@@ -17906,13 +18269,13 @@ function XViewScene({
17906
18269
  animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
17907
18270
  }
17908
18271
  },
17909
- /* @__PURE__ */ import_react28.default.createElement("style", null, `
18272
+ /* @__PURE__ */ import_react29.default.createElement("style", null, `
17910
18273
  @keyframes fadeInDown {
17911
18274
  from { opacity: 0; transform: translate(-50%, -20px); }
17912
18275
  to { opacity: 1; transform: translate(-50%, 0); }
17913
18276
  }
17914
18277
  `),
17915
- /* @__PURE__ */ import_react28.default.createElement(
18278
+ /* @__PURE__ */ import_react29.default.createElement(
17916
18279
  "svg",
17917
18280
  {
17918
18281
  width: "20",
@@ -17924,12 +18287,12 @@ function XViewScene({
17924
18287
  strokeLinecap: "round",
17925
18288
  strokeLinejoin: "round"
17926
18289
  },
17927
- /* @__PURE__ */ import_react28.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
17928
- /* @__PURE__ */ import_react28.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
17929
- /* @__PURE__ */ import_react28.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
18290
+ /* @__PURE__ */ import_react29.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
18291
+ /* @__PURE__ */ import_react29.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
18292
+ /* @__PURE__ */ import_react29.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
17930
18293
  ),
17931
- /* @__PURE__ */ import_react28.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
17932
- /* @__PURE__ */ import_react28.default.createElement(
18294
+ /* @__PURE__ */ import_react29.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
18295
+ /* @__PURE__ */ import_react29.default.createElement(
17933
18296
  "button",
17934
18297
  {
17935
18298
  onClick: () => setInvalidTargetError(null),