@lv-x-software-house/x_view 1.2.5-dev.45 → 1.2.5-dev.47

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 +351 -29
  2. package/dist/index.mjs +351 -29
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -625,12 +625,16 @@ function XViewSidebar({
625
625
  userRole,
626
626
  viewMembers = [],
627
627
  onActivateQuestLens = () => {
628
+ },
629
+ milestones = [],
630
+ onUpdateMilestones = () => {
628
631
  }
629
632
  }) {
630
633
  const [query, setQuery] = (0, import_react2.useState)("");
631
634
  const [showList, setShowList] = (0, import_react2.useState)(false);
632
635
  const [selectedNodeId, setSelectedNodeId] = (0, import_react2.useState)(null);
633
636
  const [isSaving, setIsSaving] = (0, import_react2.useState)(false);
637
+ const [newMilestoneName, setNewMilestoneName] = (0, import_react2.useState)("");
634
638
  const [activeFilters, setActiveFilters] = (0, import_react2.useState)([]);
635
639
  const [isFilterMenuOpen, setIsFilterMenuOpen] = (0, import_react2.useState)(false);
636
640
  const [filterCategory, setFilterCategory] = (0, import_react2.useState)("Type");
@@ -842,6 +846,28 @@ function XViewSidebar({
842
846
  setIsTypeDropdownOpen(false);
843
847
  }
844
848
  };
849
+ const handleAddMilestone = () => {
850
+ if (!newMilestoneName.trim()) return;
851
+ const newMilestone = {
852
+ id: `ms_${Date.now()}`,
853
+ name: newMilestoneName.trim(),
854
+ status: "active"
855
+ };
856
+ onUpdateMilestones([...milestones, newMilestone]);
857
+ setNewMilestoneName("");
858
+ };
859
+ const handleToggleMilestoneStatus = (id) => {
860
+ const updated = milestones.map(
861
+ (ms) => ms.id === id ? { ...ms, status: ms.status === "active" ? "inactive" : "active" } : ms
862
+ );
863
+ onUpdateMilestones(updated);
864
+ };
865
+ const handleRemoveMilestone = (id) => {
866
+ var _a;
867
+ if (!confirm(`Deseja remover o milestone "${(_a = milestones.find((m) => m.id === id)) == null ? void 0 : _a.name}"?`)) return;
868
+ const updated = milestones.filter((ms) => ms.id !== id);
869
+ onUpdateMilestones(updated);
870
+ };
845
871
  const ToggleButton = /* @__PURE__ */ import_react2.default.createElement(
846
872
  "button",
847
873
  {
@@ -1098,11 +1124,59 @@ function XViewSidebar({
1098
1124
  onClick: () => {
1099
1125
  onActivateQuestLens({ assigneeId: questFilterAssignee, status: questFilterStatus });
1100
1126
  setIsQuestLensMenuOpen(false);
1127
+ onToggle(false);
1101
1128
  },
1102
1129
  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"
1103
1130
  },
1104
1131
  "Aplicar Lente"
1105
- )))))), /* @__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(
1132
+ ))))), /* @__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("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Milestones")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ import_react2.default.createElement(
1133
+ "input",
1134
+ {
1135
+ type: "text",
1136
+ value: newMilestoneName,
1137
+ onChange: (e) => setNewMilestoneName(e.target.value),
1138
+ onKeyDown: (e) => e.key === "Enter" && handleAddMilestone(),
1139
+ placeholder: "Novo Milestone...",
1140
+ className: "flex-1 bg-slate-900/50 border border-white/10 rounded-md px-2 py-1.5 text-xs text-slate-100 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500/50"
1141
+ }
1142
+ ), /* @__PURE__ */ import_react2.default.createElement(
1143
+ "button",
1144
+ {
1145
+ onClick: handleAddMilestone,
1146
+ disabled: !newMilestoneName.trim(),
1147
+ className: "p-1.5 rounded-md bg-indigo-500/10 hover:bg-indigo-500/20 border border-indigo-500/30 text-indigo-300 disabled:opacity-30 transition-colors"
1148
+ },
1149
+ /* @__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("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }))
1150
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1.5 max-h-[200px] overflow-y-auto custom-scrollbar pr-1" }, milestones.length > 0 ? milestones.map((ms) => /* @__PURE__ */ import_react2.default.createElement(
1151
+ "div",
1152
+ {
1153
+ key: ms.id,
1154
+ className: `group flex items-center justify-between px-2.5 py-2 rounded-lg border transition-all ${ms.status === "active" ? "bg-indigo-500/5 border-indigo-500/20" : "bg-slate-900/30 border-white/5 opacity-60"}`
1155
+ },
1156
+ /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react2.default.createElement(
1157
+ "div",
1158
+ {
1159
+ className: `w-1.5 h-1.5 rounded-full shrink-0 ${ms.status === "active" ? "bg-indigo-400 shadow-[0_0_8px_rgba(129,140,248,0.5)]" : "bg-slate-600"}`
1160
+ }
1161
+ ), /* @__PURE__ */ import_react2.default.createElement("span", { className: `text-[11px] truncate font-medium ${ms.status === "active" ? "text-slate-100" : "text-slate-500 line-through"}` }, ms.name)),
1162
+ /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react2.default.createElement(
1163
+ "button",
1164
+ {
1165
+ onClick: () => handleToggleMilestoneStatus(ms.id),
1166
+ className: `p-1 rounded hover:bg-white/10 transition-colors ${ms.status === "active" ? "text-slate-400" : "text-indigo-400"}`,
1167
+ title: ms.status === "active" ? "Inativar" : "Ativar"
1168
+ },
1169
+ ms.status === "active" ? /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("rect", { x: "6", y: "4", width: "4", height: "16" }), /* @__PURE__ */ import_react2.default.createElement("rect", { x: "14", y: "4", width: "4", height: "16" })) : /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polygon", { points: "5 3 19 12 5 21 5 3" }))
1170
+ ), /* @__PURE__ */ import_react2.default.createElement(
1171
+ "button",
1172
+ {
1173
+ onClick: () => handleRemoveMilestone(ms.id),
1174
+ className: "p-1 rounded hover:bg-red-500/20 text-slate-500 hover:text-red-400 transition-colors",
1175
+ title: "Remover"
1176
+ },
1177
+ /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react2.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" }))
1178
+ ))
1179
+ )) : /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[10px] text-slate-600 text-center py-2 italic font-light italic" }, "Nenhum milestone definido")))), /* @__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(
1106
1180
  "button",
1107
1181
  {
1108
1182
  onClick: handleSaveClick,
@@ -2331,7 +2405,8 @@ var userActionHandlers = {
2331
2405
  nodes: sceneDataRef.current.nodes,
2332
2406
  links: sceneDataRef.current.links,
2333
2407
  quest_nodes: specificParentData.nodes,
2334
- quest_links: specificParentData.links
2408
+ quest_links: specificParentData.links,
2409
+ milestones: sceneDataRef.current.milestones || []
2335
2410
  };
2336
2411
  await context.actions.save_view_data(
2337
2412
  context.sceneSaveUrl,
@@ -2589,7 +2664,8 @@ var userActionHandlers = {
2589
2664
  nodes: sceneDataRef.current.nodes,
2590
2665
  links: sceneDataRef.current.links,
2591
2666
  quest_nodes: specificParentData.nodes,
2592
- quest_links: specificParentData.links
2667
+ quest_links: specificParentData.links,
2668
+ milestones: sceneDataRef.current.milestones || []
2593
2669
  };
2594
2670
  await context.actions.save_view_data(
2595
2671
  context.sceneSaveUrl,
@@ -2745,7 +2821,8 @@ var userActionHandlers = {
2745
2821
  nodes: sceneDataRef.current.nodes,
2746
2822
  links: sceneDataRef.current.links,
2747
2823
  quest_nodes: data.nodes,
2748
- quest_links: data.links
2824
+ quest_links: data.links,
2825
+ milestones: sceneDataRef.current.milestones || []
2749
2826
  };
2750
2827
  return context.actions.save_view_data(
2751
2828
  context.sceneSaveUrl,
@@ -3359,6 +3436,7 @@ var userActionHandlers = {
3359
3436
  links: sceneDataRef.current.links,
3360
3437
  quest_nodes: specificParentData.nodes,
3361
3438
  quest_links: specificParentData.links,
3439
+ milestones: sceneDataRef.current.milestones || [],
3362
3440
  quest_counter: sceneDataRef.current.quest_counter || 1
3363
3441
  };
3364
3442
  await context.actions.save_view_data(
@@ -3431,7 +3509,8 @@ var userActionHandlers = {
3431
3509
  links: sceneDataRef.current.links,
3432
3510
  quest_nodes: specificParentData.nodes,
3433
3511
  quest_links: specificParentData.links,
3434
- quest_counter: sceneDataRef.current.quest_counter || 1
3512
+ quest_counter: sceneDataRef.current.quest_counter || 1,
3513
+ milestones: sceneDataRef.current.milestones || []
3435
3514
  };
3436
3515
  await context.actions.save_view_data(
3437
3516
  context.sceneSaveUrl,
@@ -9467,11 +9546,15 @@ function InSceneQuestForm({
9467
9546
  questCounter = 1,
9468
9547
  // NOVA PROP
9469
9548
  viewMembers = [],
9470
- session = null
9549
+ session = null,
9550
+ milestones = [],
9551
+ onUpdateMilestones = () => {
9552
+ }
9471
9553
  }) {
9472
- var _a, _b;
9554
+ var _a, _b, _c;
9473
9555
  const [name, setName] = (0, import_react17.useState)("");
9474
9556
  const [assigneeId, setAssigneeId] = (0, import_react17.useState)("");
9557
+ const [milestoneId, setMilestoneId] = (0, import_react17.useState)("");
9475
9558
  const [types, setTypes] = (0, import_react17.useState)(["quest"]);
9476
9559
  const [typeInput, setTypeInput] = (0, import_react17.useState)("");
9477
9560
  const [status, setStatus] = (0, import_react17.useState)("Backlog");
@@ -9493,6 +9576,32 @@ function InSceneQuestForm({
9493
9576
  const [customProps, setCustomProps] = (0, import_react17.useState)([]);
9494
9577
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react17.useState)(false);
9495
9578
  const propsEndRef = (0, import_react17.useRef)(null);
9579
+ const [isMilestoneDropdownOpen, setIsMilestoneDropdownOpen] = (0, import_react17.useState)(false);
9580
+ const [milestoneSearchQuery, setMilestoneSearchQuery] = (0, import_react17.useState)("");
9581
+ const activeMilestones = import_react17.default.useMemo(
9582
+ () => (milestones || []).filter((ms) => ms.status === "active"),
9583
+ [milestones]
9584
+ );
9585
+ const filteredMilestones = import_react17.default.useMemo(() => {
9586
+ if (!milestoneSearchQuery.trim()) return activeMilestones;
9587
+ return activeMilestones.filter(
9588
+ (ms) => ms.name.toLowerCase().includes(milestoneSearchQuery.toLowerCase())
9589
+ );
9590
+ }, [activeMilestones, milestoneSearchQuery]);
9591
+ const handleSelectMilestone = (id) => {
9592
+ setMilestoneId(id);
9593
+ setIsMilestoneDropdownOpen(false);
9594
+ setMilestoneSearchQuery("");
9595
+ };
9596
+ const handleCreateMilestone = async (name2) => {
9597
+ if (!name2.trim()) return;
9598
+ const newId = `ms_${Date.now()}`;
9599
+ const newMs = { id: newId, name: name2.trim(), status: "active" };
9600
+ await onUpdateMilestones([...milestones, newMs]);
9601
+ setMilestoneId(newId);
9602
+ setIsMilestoneDropdownOpen(false);
9603
+ setMilestoneSearchQuery("");
9604
+ };
9496
9605
  const standardizedName = `${viewName} - ${questCounter} - \xBB ${name || "Nova Quest"}`;
9497
9606
  const handleAddProp = () => {
9498
9607
  const newProp = createNewCustomProperty(customProps);
@@ -9560,6 +9669,7 @@ function InSceneQuestForm({
9560
9669
  timeline: initialTimeline,
9561
9670
  Date: questDate ? { ...questDate, confirmed: true } : null,
9562
9671
  recurrence_config: recurrenceConfig,
9672
+ milestone_id: milestoneId || null,
9563
9673
  ...additionalData
9564
9674
  });
9565
9675
  };
@@ -9663,6 +9773,63 @@ function InSceneQuestForm({
9663
9773
  }
9664
9774
  ),
9665
9775
  s
9776
+ )))))), /* @__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(
9777
+ "button",
9778
+ {
9779
+ type: "button",
9780
+ onClick: () => setIsMilestoneDropdownOpen(!isMilestoneDropdownOpen),
9781
+ 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"
9782
+ },
9783
+ /* @__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")),
9784
+ /* @__PURE__ */ import_react17.default.createElement(
9785
+ import_fi15.FiChevronDown,
9786
+ {
9787
+ className: `text-slate-400 transition-transform duration-200 ${isMilestoneDropdownOpen ? "rotate-180" : ""}`
9788
+ }
9789
+ )
9790
+ ), isMilestoneDropdownOpen && /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement(
9791
+ "div",
9792
+ {
9793
+ className: "fixed inset-0 z-40",
9794
+ onClick: () => {
9795
+ setIsMilestoneDropdownOpen(false);
9796
+ setMilestoneSearchQuery("");
9797
+ }
9798
+ }
9799
+ ), /* @__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(
9800
+ "input",
9801
+ {
9802
+ type: "text",
9803
+ autoFocus: true,
9804
+ placeholder: "Buscar ou criar milestone...",
9805
+ value: milestoneSearchQuery,
9806
+ onChange: (e) => setMilestoneSearchQuery(e.target.value),
9807
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9808
+ onClick: (e) => e.stopPropagation()
9809
+ }
9810
+ )), /* @__PURE__ */ import_react17.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement(
9811
+ "li",
9812
+ {
9813
+ onClick: () => handleSelectMilestone(""),
9814
+ 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"}`
9815
+ },
9816
+ "Nenhum"
9817
+ ), filteredMilestones.map((ms) => /* @__PURE__ */ import_react17.default.createElement(
9818
+ "li",
9819
+ {
9820
+ key: ms.id,
9821
+ onClick: () => handleSelectMilestone(ms.id),
9822
+ 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"}`
9823
+ },
9824
+ ms.name
9825
+ )), milestoneSearchQuery.trim() && !filteredMilestones.some((ms) => ms.name.toLowerCase() === milestoneSearchQuery.toLowerCase().trim()) && /* @__PURE__ */ import_react17.default.createElement(
9826
+ "li",
9827
+ {
9828
+ onClick: () => handleCreateMilestone(milestoneSearchQuery),
9829
+ 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"
9830
+ },
9831
+ /* @__PURE__ */ import_react17.default.createElement("span", { className: "text-[10px] uppercase font-bold opacity-60" }, "Criar Novo Milestone"),
9832
+ /* @__PURE__ */ import_react17.default.createElement("span", { className: "font-medium" }, '"', milestoneSearchQuery, '"')
9666
9833
  )))))), /* @__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(
9667
9834
  "button",
9668
9835
  {
@@ -9670,7 +9837,7 @@ function InSceneQuestForm({
9670
9837
  onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9671
9838
  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"
9672
9839
  },
9673
- /* @__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")),
9840
+ /* @__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")),
9674
9841
  /* @__PURE__ */ import_react17.default.createElement(
9675
9842
  import_fi15.FiChevronDown,
9676
9843
  {
@@ -10642,9 +10809,12 @@ function QuestDetailsPanel({
10642
10809
  userRole,
10643
10810
  currentDatasetName,
10644
10811
  viewMembers = [],
10645
- currentUser
10812
+ currentUser,
10813
+ milestones = [],
10814
+ onUpdateMilestones = () => {
10815
+ }
10646
10816
  }) {
10647
- var _a, _b;
10817
+ var _a, _b, _c;
10648
10818
  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) || "";
10649
10819
  const [rawTitle, setRawTitle] = (0, import_react20.useState)(initialRawTitle);
10650
10820
  const prefixParts = ((node == null ? void 0 : node.name) || "").split(" - \xBB ");
@@ -10657,6 +10827,7 @@ function QuestDetailsPanel({
10657
10827
  const [assigneeId, setAssigneeId] = (0, import_react20.useState)((node == null ? void 0 : node.assignee_id) || "");
10658
10828
  const [description, setDescription] = (0, import_react20.useState)((node == null ? void 0 : node.description) ?? "");
10659
10829
  const [intensity, setIntensity] = (0, import_react20.useState)((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
10830
+ const [milestoneId, setMilestoneId] = (0, import_react20.useState)((node == null ? void 0 : node.milestone_id) || "");
10660
10831
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react20.useState)(false);
10661
10832
  const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react20.useState)(false);
10662
10833
  const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react20.useState)("");
@@ -10694,6 +10865,35 @@ function QuestDetailsPanel({
10694
10865
  minWidth: 320,
10695
10866
  maxWidth: maxPanelW
10696
10867
  });
10868
+ const [isMilestoneDropdownOpen, setIsMilestoneDropdownOpen] = (0, import_react20.useState)(false);
10869
+ const [milestoneSearchQuery, setMilestoneSearchQuery] = (0, import_react20.useState)("");
10870
+ const activeMilestones = import_react20.default.useMemo(
10871
+ () => (milestones || []).filter((ms) => ms.status === "active"),
10872
+ [milestones]
10873
+ );
10874
+ const filteredMilestones = import_react20.default.useMemo(() => {
10875
+ const search = milestoneSearchQuery.toLowerCase().trim();
10876
+ if (!search) return activeMilestones;
10877
+ return activeMilestones.filter(
10878
+ (ms) => ms.name.toLowerCase().includes(search)
10879
+ );
10880
+ }, [activeMilestones, milestoneSearchQuery]);
10881
+ const handleSelectMilestone = (id) => {
10882
+ setMilestoneId(id);
10883
+ setIsMilestoneDropdownOpen(false);
10884
+ setMilestoneSearchQuery("");
10885
+ setHasUnsavedChanges(true);
10886
+ };
10887
+ const handleCreateMilestone = async (name) => {
10888
+ if (!name.trim()) return;
10889
+ const newId = `ms_${Date.now()}`;
10890
+ const newMs = { id: newId, name: name.trim(), status: "active" };
10891
+ await onUpdateMilestones([...milestones, newMs]);
10892
+ setMilestoneId(newId);
10893
+ setIsMilestoneDropdownOpen(false);
10894
+ setMilestoneSearchQuery("");
10895
+ setHasUnsavedChanges(true);
10896
+ };
10697
10897
  (0, import_react20.useEffect)(() => {
10698
10898
  setWidth(isReadMode ? 700 : 440);
10699
10899
  }, [isReadMode, setWidth]);
@@ -10719,6 +10919,7 @@ function QuestDetailsPanel({
10719
10919
  setReturnAssigneeId("");
10720
10920
  setDescription((node == null ? void 0 : node.description) ?? "");
10721
10921
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
10922
+ setMilestoneId((node == null ? void 0 : node.milestone_id) || "");
10722
10923
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
10723
10924
  setCustomProps(extractCustomPropsFromNode(node || {}));
10724
10925
  setReviewStack((node == null ? void 0 : node.reviewStack) || []);
@@ -10731,6 +10932,7 @@ function QuestDetailsPanel({
10731
10932
  custom_date: "",
10732
10933
  next_assignee_id: ""
10733
10934
  });
10935
+ setMilestoneId((node == null ? void 0 : node.milestone_id) || "");
10734
10936
  setHasUnsavedChanges(false);
10735
10937
  }
10736
10938
  }, [node]);
@@ -11029,7 +11231,8 @@ ${noteText}` : noteText : entry.notes,
11029
11231
  reviewStack: currentReviewStack,
11030
11232
  timeline: currentTimeline,
11031
11233
  ...extrasObj,
11032
- version_node: node.version_node
11234
+ version_node: node.version_node,
11235
+ milestone_id: milestoneId || null
11033
11236
  };
11034
11237
  await onSave(dataToSave, keepOpen);
11035
11238
  onDataUpdate(dataToSave);
@@ -11231,7 +11434,51 @@ ${noteText}` : noteText : entry.notes,
11231
11434
  maxLength: "10",
11232
11435
  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"
11233
11436
  }
11234
- ))))), /* @__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(
11437
+ ))))), /* @__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(
11438
+ "button",
11439
+ {
11440
+ type: "button",
11441
+ onClick: () => canEdit && setIsMilestoneDropdownOpen(!isMilestoneDropdownOpen),
11442
+ disabled: !canEdit,
11443
+ 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"}`
11444
+ },
11445
+ /* @__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")),
11446
+ canEdit && /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isMilestoneDropdownOpen ? "rotate-180" : ""}` })
11447
+ ), 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(
11448
+ "input",
11449
+ {
11450
+ type: "text",
11451
+ autoFocus: true,
11452
+ placeholder: "Buscar ou criar milestone...",
11453
+ value: milestoneSearchQuery,
11454
+ onChange: (e) => setMilestoneSearchQuery(e.target.value),
11455
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
11456
+ onClick: (e) => e.stopPropagation()
11457
+ }
11458
+ )), /* @__PURE__ */ import_react20.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement(
11459
+ "li",
11460
+ {
11461
+ onClick: () => handleSelectMilestone(""),
11462
+ 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"}`
11463
+ },
11464
+ "Nenhum"
11465
+ ), filteredMilestones.map((ms) => /* @__PURE__ */ import_react20.default.createElement(
11466
+ "li",
11467
+ {
11468
+ key: ms.id,
11469
+ onClick: () => handleSelectMilestone(ms.id),
11470
+ 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"}`
11471
+ },
11472
+ ms.name
11473
+ )), milestoneSearchQuery.trim() && !filteredMilestones.some((ms) => ms.name.toLowerCase() === milestoneSearchQuery.toLowerCase().trim()) && /* @__PURE__ */ import_react20.default.createElement(
11474
+ "li",
11475
+ {
11476
+ onClick: () => handleCreateMilestone(milestoneSearchQuery),
11477
+ 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"
11478
+ },
11479
+ /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-[10px] uppercase font-bold opacity-60" }, "Criar Novo Milestone"),
11480
+ /* @__PURE__ */ import_react20.default.createElement("span", { className: "font-medium" }, '"', milestoneSearchQuery, '"')
11481
+ )))))), /* @__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(
11235
11482
  "button",
11236
11483
  {
11237
11484
  type: "button",
@@ -11417,7 +11664,7 @@ ${noteText}` : noteText : entry.notes,
11417
11664
  onClick: () => setIsReturnDropdownOpen(!isReturnDropdownOpen),
11418
11665
  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"
11419
11666
  },
11420
- /* @__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...")),
11667
+ /* @__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...")),
11421
11668
  /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isReturnDropdownOpen ? "rotate-180" : ""}`, size: 14 })
11422
11669
  ), isReturnDropdownOpen && /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
11423
11670
  setIsReturnDropdownOpen(false);
@@ -13085,7 +13332,7 @@ function XViewScene({
13085
13332
  check_user_permission,
13086
13333
  get_view_members
13087
13334
  }) {
13088
- var _a, _b, _c, _d, _e, _f, _g, _h;
13335
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
13089
13336
  const { data: session, status } = (0, import_react29.useSession)();
13090
13337
  const router = (0, import_navigation.useRouter)();
13091
13338
  const searchParams = (0, import_navigation.useSearchParams)();
@@ -13914,20 +14161,23 @@ function XViewScene({
13914
14161
  sceneResponse.data.scene.nodes = [];
13915
14162
  sceneResponse.data.scene.links = [];
13916
14163
  }
13917
- sceneDataRef.current = sceneResponse.data.scene;
14164
+ sceneDataRef.current = {
14165
+ ...sceneResponse.data.scene,
14166
+ milestones: sceneResponse.data.scene.milestones || []
14167
+ };
13918
14168
  parentDataRef.current = sceneResponse.data.parent;
13919
14169
  ancestryDataRef.current = sceneResponse.data.ancestry;
13920
14170
  console.log(
13921
14171
  "Console de sceneResponse.data.scene:",
13922
- sceneResponse.data.scene
14172
+ sceneDataRef.current
13923
14173
  );
13924
14174
  console.log(
13925
14175
  "Console de sceneResponse.data.parent:",
13926
- sceneResponse.data.parent
14176
+ parentDataRef.current
13927
14177
  );
13928
14178
  console.log(
13929
14179
  "Console de sceneResponse.data.ancestry:",
13930
- sceneResponse.data.ancestry
14180
+ ancestryDataRef.current
13931
14181
  );
13932
14182
  setIsInitialized(true);
13933
14183
  } else {
@@ -14059,6 +14309,9 @@ function XViewScene({
14059
14309
  timeToYMap.set(ms, idx * TIMELINE_LAYER_SPACING_Y);
14060
14310
  });
14061
14311
  const startX = -((QUEST_STATUS_ORDER.length - 1) * QUEST_LENS_COLUMN_SPACING) / 2;
14312
+ let sumX = 0;
14313
+ let sumY = 0;
14314
+ let questCount = 0;
14062
14315
  const positionGroups = /* @__PURE__ */ new Map();
14063
14316
  const NODE_X_GAP = 22;
14064
14317
  QUEST_STATUS_ORDER.forEach((statusName) => {
@@ -14162,6 +14415,9 @@ function XViewScene({
14162
14415
  const lensTween = new import_tween2.Tween(mesh.position).to({ x: finalX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
14163
14416
  tweenGroup.add(lensTween);
14164
14417
  lensTween.start();
14418
+ sumX += finalX;
14419
+ sumY += targetY;
14420
+ questCount++;
14165
14421
  if (!timeInfo.isUndated && timeInfo.label && !mesh.getObjectByName("questDateLabel")) {
14166
14422
  const dateSprite = createTextSprite(timeInfo.label, 36);
14167
14423
  dateSprite.name = "questDateLabel";
@@ -14210,6 +14466,18 @@ function XViewScene({
14210
14466
  }
14211
14467
  });
14212
14468
  });
14469
+ if (questCount > 0) {
14470
+ const { camera, controls } = stateRef.current;
14471
+ const centerX = sumX / questCount;
14472
+ const centerY = sumY / questCount;
14473
+ const targetFocus = new THREE3.Vector3(centerX, centerY, 0);
14474
+ const targetTween = new import_tween2.Tween(controls.target).to({ x: targetFocus.x, y: targetFocus.y, z: targetFocus.z }, 1500).easing(import_tween2.Easing.Cubic.InOut);
14475
+ tweenGroup.add(targetTween);
14476
+ targetTween.start();
14477
+ const camTween = new import_tween2.Tween(camera.position).to({ x: targetFocus.x, y: targetFocus.y, z: 140 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
14478
+ tweenGroup.add(camTween);
14479
+ camTween.start();
14480
+ }
14213
14481
  }, [addOrUpdateNodeMesh]);
14214
14482
  (0, import_react28.useEffect)(() => {
14215
14483
  if (!isInitialized || !sceneDataRef.current) return;
@@ -15432,7 +15700,8 @@ function XViewScene({
15432
15700
  links: sceneDataRef2.current.links,
15433
15701
  quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
15434
15702
  quest_links: graphDataRef.current[sceneConfigId2].links,
15435
- quest_counter: currentCounter + 1
15703
+ quest_counter: currentCounter + 1,
15704
+ milestones: sceneDataRef2.current.milestones || []
15436
15705
  };
15437
15706
  try {
15438
15707
  await actions.save_view_data(sceneSaveUrl2, sceneFileData);
@@ -17084,6 +17353,52 @@ function XViewScene({
17084
17353
  },
17085
17354
  [actionHandlerContext]
17086
17355
  );
17356
+ const handleUpdateMilestones = (0, import_react28.useCallback)(
17357
+ async (newMilestones) => {
17358
+ var _a2, _b2, _c2;
17359
+ if (!sceneDataRef.current) return;
17360
+ sceneDataRef.current.milestones = newMilestones;
17361
+ setSceneVersion((v) => v + 1);
17362
+ const isView = ((_a2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _a2.toLowerCase()) === "view";
17363
+ if (!isView || !save_view_data || !sceneSaveUrl) return;
17364
+ const { nodeObjects, allLinks } = stateRef.current;
17365
+ const currentNodes = Object.values(nodeObjects || {}).map((mesh) => {
17366
+ const {
17367
+ _baseEmissiveIntensity,
17368
+ labelObject,
17369
+ labelOffset,
17370
+ timelineIntervalBar,
17371
+ timelineEndLabel,
17372
+ ...serializableNodeData
17373
+ } = mesh.userData;
17374
+ return serializableNodeData;
17375
+ });
17376
+ const currentLinks = (allLinks || []).map((line) => {
17377
+ const { sourceNode, targetNode, ...serializableLinkData } = line.userData;
17378
+ return serializableLinkData;
17379
+ });
17380
+ const sceneFileData = {
17381
+ parent_dbs: sceneDataRef.current.parent_dbs,
17382
+ nodes: currentNodes,
17383
+ links: currentLinks,
17384
+ quest_nodes: ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || sceneDataRef.current.quest_nodes || [],
17385
+ quest_links: ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || sceneDataRef.current.quest_links || [],
17386
+ quest_counter: sceneDataRef.current.quest_counter || 1,
17387
+ milestones: newMilestones
17388
+ };
17389
+ try {
17390
+ await save_view_data(sceneSaveUrl, sceneFileData);
17391
+ } catch (error) {
17392
+ console.error("Erro ao salvar milestones:", error);
17393
+ }
17394
+ },
17395
+ [
17396
+ sceneConfigId,
17397
+ sceneSaveUrl,
17398
+ save_view_data,
17399
+ viewParams == null ? void 0 : viewParams.type
17400
+ ]
17401
+ );
17087
17402
  const handleSaveCurrentView = (0, import_react28.useCallback)(async () => {
17088
17403
  var _a2, _b2, _c2;
17089
17404
  const { nodeObjects, allLinks } = stateRef.current;
@@ -17118,7 +17433,8 @@ function XViewScene({
17118
17433
  links: currentLinks,
17119
17434
  quest_nodes: isView ? ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || [] : sceneDataRef.current.quest_nodes || [],
17120
17435
  quest_links: isView ? ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || [] : sceneDataRef.current.quest_links || [],
17121
- quest_counter: sceneDataRef.current.quest_counter || 1
17436
+ quest_counter: sceneDataRef.current.quest_counter || 1,
17437
+ milestones: sceneDataRef.current.milestones || []
17122
17438
  };
17123
17439
  try {
17124
17440
  await save_view_data(sceneSaveUrl, sceneFileData);
@@ -17455,7 +17771,9 @@ function XViewScene({
17455
17771
  isOpen: isSidebarOpen,
17456
17772
  onToggle: setIsSidebarOpen,
17457
17773
  userRole: userPermissionRole,
17458
- viewMembers
17774
+ viewMembers,
17775
+ milestones: ((_c = sceneDataRef.current) == null ? void 0 : _c.milestones) || [],
17776
+ onUpdateMilestones: handleUpdateMilestones
17459
17777
  }
17460
17778
  ),
17461
17779
  creationMode.isActive && /* @__PURE__ */ import_react28.default.createElement(
@@ -17479,8 +17797,8 @@ function XViewScene({
17479
17797
  },
17480
17798
  refEl: formRef,
17481
17799
  existingTypes: existingNodeTypes,
17482
- initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
17483
- sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
17800
+ initialColor: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.color,
17801
+ sourceTypes: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.type,
17484
17802
  onIntensityChange: handleGhostNodeIntensityChange,
17485
17803
  onUploadFile: upload_file_action,
17486
17804
  availableDatasets,
@@ -17509,8 +17827,8 @@ function XViewScene({
17509
17827
  transition: "opacity 200ms ease-out"
17510
17828
  },
17511
17829
  refEl: formRef,
17512
- fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
17513
- fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
17830
+ fixedType: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.type,
17831
+ fixedColor: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.color,
17514
17832
  onUploadFile: upload_file_action,
17515
17833
  availableNodes: allAvailableNodes,
17516
17834
  availableAncestries: allAvailableAncestries
@@ -17538,8 +17856,10 @@ function XViewScene({
17538
17856
  availableNodes: allAvailableNodes,
17539
17857
  availableAncestries: allAvailableAncestries,
17540
17858
  viewName: viewParams == null ? void 0 : viewParams.name,
17541
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
17542
- viewMembers
17859
+ questCounter: ((_h = sceneDataRef.current) == null ? void 0 : _h.quest_counter) || 1,
17860
+ viewMembers,
17861
+ milestones: ((_i = sceneDataRef.current) == null ? void 0 : _i.milestones) || [],
17862
+ onUpdateMilestones: handleUpdateMilestones
17543
17863
  }
17544
17864
  ),
17545
17865
  readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react28.default.createElement(
@@ -17671,7 +17991,9 @@ function XViewScene({
17671
17991
  userRole: userPermissionRole,
17672
17992
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
17673
17993
  viewMembers,
17674
- currentUser: session == null ? void 0 : session.user
17994
+ currentUser: session == null ? void 0 : session.user,
17995
+ milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
17996
+ onUpdateMilestones: handleUpdateMilestones
17675
17997
  }
17676
17998
  ),
17677
17999
  detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react28.default.createElement(
@@ -17856,7 +18178,7 @@ function XViewScene({
17856
18178
  onClose: () => setIsImportModalOpen(false),
17857
18179
  onConfirm: handleConfirmImport,
17858
18180
  session,
17859
- parentDbs: ((_h = sceneDataRef.current) == null ? void 0 : _h.parent_dbs) || [],
18181
+ parentDbs: ((_k = sceneDataRef.current) == null ? void 0 : _k.parent_dbs) || [],
17860
18182
  onFetchAvailableFiles: import_parent_file_modal_get,
17861
18183
  currentViewName: viewParams == null ? void 0 : viewParams.name,
17862
18184
  currentAncestries: ancestryDataRef.current || []
package/dist/index.mjs CHANGED
@@ -586,12 +586,16 @@ function XViewSidebar({
586
586
  userRole,
587
587
  viewMembers = [],
588
588
  onActivateQuestLens = () => {
589
+ },
590
+ milestones = [],
591
+ onUpdateMilestones = () => {
589
592
  }
590
593
  }) {
591
594
  const [query, setQuery] = useState2("");
592
595
  const [showList, setShowList] = useState2(false);
593
596
  const [selectedNodeId, setSelectedNodeId] = useState2(null);
594
597
  const [isSaving, setIsSaving] = useState2(false);
598
+ const [newMilestoneName, setNewMilestoneName] = useState2("");
595
599
  const [activeFilters, setActiveFilters] = useState2([]);
596
600
  const [isFilterMenuOpen, setIsFilterMenuOpen] = useState2(false);
597
601
  const [filterCategory, setFilterCategory] = useState2("Type");
@@ -803,6 +807,28 @@ function XViewSidebar({
803
807
  setIsTypeDropdownOpen(false);
804
808
  }
805
809
  };
810
+ const handleAddMilestone = () => {
811
+ if (!newMilestoneName.trim()) return;
812
+ const newMilestone = {
813
+ id: `ms_${Date.now()}`,
814
+ name: newMilestoneName.trim(),
815
+ status: "active"
816
+ };
817
+ onUpdateMilestones([...milestones, newMilestone]);
818
+ setNewMilestoneName("");
819
+ };
820
+ const handleToggleMilestoneStatus = (id) => {
821
+ const updated = milestones.map(
822
+ (ms) => ms.id === id ? { ...ms, status: ms.status === "active" ? "inactive" : "active" } : ms
823
+ );
824
+ onUpdateMilestones(updated);
825
+ };
826
+ const handleRemoveMilestone = (id) => {
827
+ var _a;
828
+ if (!confirm(`Deseja remover o milestone "${(_a = milestones.find((m) => m.id === id)) == null ? void 0 : _a.name}"?`)) return;
829
+ const updated = milestones.filter((ms) => ms.id !== id);
830
+ onUpdateMilestones(updated);
831
+ };
806
832
  const ToggleButton = /* @__PURE__ */ React2.createElement(
807
833
  "button",
808
834
  {
@@ -1059,11 +1085,59 @@ function XViewSidebar({
1059
1085
  onClick: () => {
1060
1086
  onActivateQuestLens({ assigneeId: questFilterAssignee, status: questFilterStatus });
1061
1087
  setIsQuestLensMenuOpen(false);
1088
+ onToggle(false);
1062
1089
  },
1063
1090
  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"
1064
1091
  },
1065
1092
  "Aplicar Lente"
1066
- )))))), /* @__PURE__ */ React2.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ React2.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ React2.createElement(
1093
+ ))))), /* @__PURE__ */ React2.createElement("div", { className: "h-px bg-white/10" }), /* @__PURE__ */ React2.createElement("section", { className: "space-y-3" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Milestones")), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React2.createElement(
1094
+ "input",
1095
+ {
1096
+ type: "text",
1097
+ value: newMilestoneName,
1098
+ onChange: (e) => setNewMilestoneName(e.target.value),
1099
+ onKeyDown: (e) => e.key === "Enter" && handleAddMilestone(),
1100
+ placeholder: "Novo Milestone...",
1101
+ className: "flex-1 bg-slate-900/50 border border-white/10 rounded-md px-2 py-1.5 text-xs text-slate-100 placeholder:text-slate-600 focus:outline-none focus:border-indigo-500/50"
1102
+ }
1103
+ ), /* @__PURE__ */ React2.createElement(
1104
+ "button",
1105
+ {
1106
+ onClick: handleAddMilestone,
1107
+ disabled: !newMilestoneName.trim(),
1108
+ className: "p-1.5 rounded-md bg-indigo-500/10 hover:bg-indigo-500/20 border border-indigo-500/30 text-indigo-300 disabled:opacity-30 transition-colors"
1109
+ },
1110
+ /* @__PURE__ */ React2.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__ */ React2.createElement("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), /* @__PURE__ */ React2.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }))
1111
+ )), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1.5 max-h-[200px] overflow-y-auto custom-scrollbar pr-1" }, milestones.length > 0 ? milestones.map((ms) => /* @__PURE__ */ React2.createElement(
1112
+ "div",
1113
+ {
1114
+ key: ms.id,
1115
+ className: `group flex items-center justify-between px-2.5 py-2 rounded-lg border transition-all ${ms.status === "active" ? "bg-indigo-500/5 border-indigo-500/20" : "bg-slate-900/30 border-white/5 opacity-60"}`
1116
+ },
1117
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React2.createElement(
1118
+ "div",
1119
+ {
1120
+ className: `w-1.5 h-1.5 rounded-full shrink-0 ${ms.status === "active" ? "bg-indigo-400 shadow-[0_0_8px_rgba(129,140,248,0.5)]" : "bg-slate-600"}`
1121
+ }
1122
+ ), /* @__PURE__ */ React2.createElement("span", { className: `text-[11px] truncate font-medium ${ms.status === "active" ? "text-slate-100" : "text-slate-500 line-through"}` }, ms.name)),
1123
+ /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity" }, /* @__PURE__ */ React2.createElement(
1124
+ "button",
1125
+ {
1126
+ onClick: () => handleToggleMilestoneStatus(ms.id),
1127
+ className: `p-1 rounded hover:bg-white/10 transition-colors ${ms.status === "active" ? "text-slate-400" : "text-indigo-400"}`,
1128
+ title: ms.status === "active" ? "Inativar" : "Ativar"
1129
+ },
1130
+ ms.status === "active" ? /* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("rect", { x: "6", y: "4", width: "4", height: "16" }), /* @__PURE__ */ React2.createElement("rect", { x: "14", y: "4", width: "4", height: "16" })) : /* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("polygon", { points: "5 3 19 12 5 21 5 3" }))
1131
+ ), /* @__PURE__ */ React2.createElement(
1132
+ "button",
1133
+ {
1134
+ onClick: () => handleRemoveMilestone(ms.id),
1135
+ className: "p-1 rounded hover:bg-red-500/20 text-slate-500 hover:text-red-400 transition-colors",
1136
+ title: "Remover"
1137
+ },
1138
+ /* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ React2.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React2.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ React2.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" }))
1139
+ ))
1140
+ )) : /* @__PURE__ */ React2.createElement("p", { className: "text-[10px] text-slate-600 text-center py-2 italic font-light italic" }, "Nenhum milestone definido")))), /* @__PURE__ */ React2.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ React2.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ React2.createElement(
1067
1141
  "button",
1068
1142
  {
1069
1143
  onClick: handleSaveClick,
@@ -2292,7 +2366,8 @@ var userActionHandlers = {
2292
2366
  nodes: sceneDataRef.current.nodes,
2293
2367
  links: sceneDataRef.current.links,
2294
2368
  quest_nodes: specificParentData.nodes,
2295
- quest_links: specificParentData.links
2369
+ quest_links: specificParentData.links,
2370
+ milestones: sceneDataRef.current.milestones || []
2296
2371
  };
2297
2372
  await context.actions.save_view_data(
2298
2373
  context.sceneSaveUrl,
@@ -2550,7 +2625,8 @@ var userActionHandlers = {
2550
2625
  nodes: sceneDataRef.current.nodes,
2551
2626
  links: sceneDataRef.current.links,
2552
2627
  quest_nodes: specificParentData.nodes,
2553
- quest_links: specificParentData.links
2628
+ quest_links: specificParentData.links,
2629
+ milestones: sceneDataRef.current.milestones || []
2554
2630
  };
2555
2631
  await context.actions.save_view_data(
2556
2632
  context.sceneSaveUrl,
@@ -2706,7 +2782,8 @@ var userActionHandlers = {
2706
2782
  nodes: sceneDataRef.current.nodes,
2707
2783
  links: sceneDataRef.current.links,
2708
2784
  quest_nodes: data.nodes,
2709
- quest_links: data.links
2785
+ quest_links: data.links,
2786
+ milestones: sceneDataRef.current.milestones || []
2710
2787
  };
2711
2788
  return context.actions.save_view_data(
2712
2789
  context.sceneSaveUrl,
@@ -3320,6 +3397,7 @@ var userActionHandlers = {
3320
3397
  links: sceneDataRef.current.links,
3321
3398
  quest_nodes: specificParentData.nodes,
3322
3399
  quest_links: specificParentData.links,
3400
+ milestones: sceneDataRef.current.milestones || [],
3323
3401
  quest_counter: sceneDataRef.current.quest_counter || 1
3324
3402
  };
3325
3403
  await context.actions.save_view_data(
@@ -3392,7 +3470,8 @@ var userActionHandlers = {
3392
3470
  links: sceneDataRef.current.links,
3393
3471
  quest_nodes: specificParentData.nodes,
3394
3472
  quest_links: specificParentData.links,
3395
- quest_counter: sceneDataRef.current.quest_counter || 1
3473
+ quest_counter: sceneDataRef.current.quest_counter || 1,
3474
+ milestones: sceneDataRef.current.milestones || []
3396
3475
  };
3397
3476
  await context.actions.save_view_data(
3398
3477
  context.sceneSaveUrl,
@@ -9486,11 +9565,15 @@ function InSceneQuestForm({
9486
9565
  questCounter = 1,
9487
9566
  // NOVA PROP
9488
9567
  viewMembers = [],
9489
- session = null
9568
+ session = null,
9569
+ milestones = [],
9570
+ onUpdateMilestones = () => {
9571
+ }
9490
9572
  }) {
9491
- var _a, _b;
9573
+ var _a, _b, _c;
9492
9574
  const [name, setName] = useState17("");
9493
9575
  const [assigneeId, setAssigneeId] = useState17("");
9576
+ const [milestoneId, setMilestoneId] = useState17("");
9494
9577
  const [types, setTypes] = useState17(["quest"]);
9495
9578
  const [typeInput, setTypeInput] = useState17("");
9496
9579
  const [status, setStatus] = useState17("Backlog");
@@ -9512,6 +9595,32 @@ function InSceneQuestForm({
9512
9595
  const [customProps, setCustomProps] = useState17([]);
9513
9596
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState17(false);
9514
9597
  const propsEndRef = useRef12(null);
9598
+ const [isMilestoneDropdownOpen, setIsMilestoneDropdownOpen] = useState17(false);
9599
+ const [milestoneSearchQuery, setMilestoneSearchQuery] = useState17("");
9600
+ const activeMilestones = React16.useMemo(
9601
+ () => (milestones || []).filter((ms) => ms.status === "active"),
9602
+ [milestones]
9603
+ );
9604
+ const filteredMilestones = React16.useMemo(() => {
9605
+ if (!milestoneSearchQuery.trim()) return activeMilestones;
9606
+ return activeMilestones.filter(
9607
+ (ms) => ms.name.toLowerCase().includes(milestoneSearchQuery.toLowerCase())
9608
+ );
9609
+ }, [activeMilestones, milestoneSearchQuery]);
9610
+ const handleSelectMilestone = (id) => {
9611
+ setMilestoneId(id);
9612
+ setIsMilestoneDropdownOpen(false);
9613
+ setMilestoneSearchQuery("");
9614
+ };
9615
+ const handleCreateMilestone = async (name2) => {
9616
+ if (!name2.trim()) return;
9617
+ const newId = `ms_${Date.now()}`;
9618
+ const newMs = { id: newId, name: name2.trim(), status: "active" };
9619
+ await onUpdateMilestones([...milestones, newMs]);
9620
+ setMilestoneId(newId);
9621
+ setIsMilestoneDropdownOpen(false);
9622
+ setMilestoneSearchQuery("");
9623
+ };
9515
9624
  const standardizedName = `${viewName} - ${questCounter} - \xBB ${name || "Nova Quest"}`;
9516
9625
  const handleAddProp = () => {
9517
9626
  const newProp = createNewCustomProperty(customProps);
@@ -9579,6 +9688,7 @@ function InSceneQuestForm({
9579
9688
  timeline: initialTimeline,
9580
9689
  Date: questDate ? { ...questDate, confirmed: true } : null,
9581
9690
  recurrence_config: recurrenceConfig,
9691
+ milestone_id: milestoneId || null,
9582
9692
  ...additionalData
9583
9693
  });
9584
9694
  };
@@ -9682,6 +9792,63 @@ function InSceneQuestForm({
9682
9792
  }
9683
9793
  ),
9684
9794
  s
9795
+ )))))), /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Milestone"), /* @__PURE__ */ React16.createElement("div", { className: "relative" }, /* @__PURE__ */ React16.createElement(
9796
+ "button",
9797
+ {
9798
+ type: "button",
9799
+ onClick: () => setIsMilestoneDropdownOpen(!isMilestoneDropdownOpen),
9800
+ 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"
9801
+ },
9802
+ /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React16.createElement(FiTarget, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React16.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = (milestones || []).find((m) => m.id === milestoneId)) == null ? void 0 : _a.name) || "Nenhum")),
9803
+ /* @__PURE__ */ React16.createElement(
9804
+ FiChevronDown6,
9805
+ {
9806
+ className: `text-slate-400 transition-transform duration-200 ${isMilestoneDropdownOpen ? "rotate-180" : ""}`
9807
+ }
9808
+ )
9809
+ ), isMilestoneDropdownOpen && /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement(
9810
+ "div",
9811
+ {
9812
+ className: "fixed inset-0 z-40",
9813
+ onClick: () => {
9814
+ setIsMilestoneDropdownOpen(false);
9815
+ setMilestoneSearchQuery("");
9816
+ }
9817
+ }
9818
+ ), /* @__PURE__ */ React16.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__ */ React16.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ React16.createElement(FiSearch5, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ React16.createElement(
9819
+ "input",
9820
+ {
9821
+ type: "text",
9822
+ autoFocus: true,
9823
+ placeholder: "Buscar ou criar milestone...",
9824
+ value: milestoneSearchQuery,
9825
+ onChange: (e) => setMilestoneSearchQuery(e.target.value),
9826
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9827
+ onClick: (e) => e.stopPropagation()
9828
+ }
9829
+ )), /* @__PURE__ */ React16.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React16.createElement(
9830
+ "li",
9831
+ {
9832
+ onClick: () => handleSelectMilestone(""),
9833
+ 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"}`
9834
+ },
9835
+ "Nenhum"
9836
+ ), filteredMilestones.map((ms) => /* @__PURE__ */ React16.createElement(
9837
+ "li",
9838
+ {
9839
+ key: ms.id,
9840
+ onClick: () => handleSelectMilestone(ms.id),
9841
+ 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"}`
9842
+ },
9843
+ ms.name
9844
+ )), milestoneSearchQuery.trim() && !filteredMilestones.some((ms) => ms.name.toLowerCase() === milestoneSearchQuery.toLowerCase().trim()) && /* @__PURE__ */ React16.createElement(
9845
+ "li",
9846
+ {
9847
+ onClick: () => handleCreateMilestone(milestoneSearchQuery),
9848
+ 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"
9849
+ },
9850
+ /* @__PURE__ */ React16.createElement("span", { className: "text-[10px] uppercase font-bold opacity-60" }, "Criar Novo Milestone"),
9851
+ /* @__PURE__ */ React16.createElement("span", { className: "font-medium" }, '"', milestoneSearchQuery, '"')
9685
9852
  )))))), /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300 font-medium uppercase tracking-wider" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ React16.createElement("div", { className: "relative" }, /* @__PURE__ */ React16.createElement(
9686
9853
  "button",
9687
9854
  {
@@ -9689,7 +9856,7 @@ function InSceneQuestForm({
9689
9856
  onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9690
9857
  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"
9691
9858
  },
9692
- /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React16.createElement(FiUser2, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React16.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")),
9859
+ /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React16.createElement(FiUser2, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React16.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")),
9693
9860
  /* @__PURE__ */ React16.createElement(
9694
9861
  FiChevronDown6,
9695
9862
  {
@@ -10673,9 +10840,12 @@ function QuestDetailsPanel({
10673
10840
  userRole,
10674
10841
  currentDatasetName,
10675
10842
  viewMembers = [],
10676
- currentUser
10843
+ currentUser,
10844
+ milestones = [],
10845
+ onUpdateMilestones = () => {
10846
+ }
10677
10847
  }) {
10678
- var _a, _b;
10848
+ var _a, _b, _c;
10679
10849
  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) || "";
10680
10850
  const [rawTitle, setRawTitle] = useState19(initialRawTitle);
10681
10851
  const prefixParts = ((node == null ? void 0 : node.name) || "").split(" - \xBB ");
@@ -10688,6 +10858,7 @@ function QuestDetailsPanel({
10688
10858
  const [assigneeId, setAssigneeId] = useState19((node == null ? void 0 : node.assignee_id) || "");
10689
10859
  const [description, setDescription] = useState19((node == null ? void 0 : node.description) ?? "");
10690
10860
  const [intensity, setIntensity] = useState19((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
10861
+ const [milestoneId, setMilestoneId] = useState19((node == null ? void 0 : node.milestone_id) || "");
10691
10862
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState19(false);
10692
10863
  const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState19(false);
10693
10864
  const [assigneeSearchQuery, setAssigneeSearchQuery] = useState19("");
@@ -10725,6 +10896,35 @@ function QuestDetailsPanel({
10725
10896
  minWidth: 320,
10726
10897
  maxWidth: maxPanelW
10727
10898
  });
10899
+ const [isMilestoneDropdownOpen, setIsMilestoneDropdownOpen] = useState19(false);
10900
+ const [milestoneSearchQuery, setMilestoneSearchQuery] = useState19("");
10901
+ const activeMilestones = React19.useMemo(
10902
+ () => (milestones || []).filter((ms) => ms.status === "active"),
10903
+ [milestones]
10904
+ );
10905
+ const filteredMilestones = React19.useMemo(() => {
10906
+ const search = milestoneSearchQuery.toLowerCase().trim();
10907
+ if (!search) return activeMilestones;
10908
+ return activeMilestones.filter(
10909
+ (ms) => ms.name.toLowerCase().includes(search)
10910
+ );
10911
+ }, [activeMilestones, milestoneSearchQuery]);
10912
+ const handleSelectMilestone = (id) => {
10913
+ setMilestoneId(id);
10914
+ setIsMilestoneDropdownOpen(false);
10915
+ setMilestoneSearchQuery("");
10916
+ setHasUnsavedChanges(true);
10917
+ };
10918
+ const handleCreateMilestone = async (name) => {
10919
+ if (!name.trim()) return;
10920
+ const newId = `ms_${Date.now()}`;
10921
+ const newMs = { id: newId, name: name.trim(), status: "active" };
10922
+ await onUpdateMilestones([...milestones, newMs]);
10923
+ setMilestoneId(newId);
10924
+ setIsMilestoneDropdownOpen(false);
10925
+ setMilestoneSearchQuery("");
10926
+ setHasUnsavedChanges(true);
10927
+ };
10728
10928
  useEffect16(() => {
10729
10929
  setWidth(isReadMode ? 700 : 440);
10730
10930
  }, [isReadMode, setWidth]);
@@ -10750,6 +10950,7 @@ function QuestDetailsPanel({
10750
10950
  setReturnAssigneeId("");
10751
10951
  setDescription((node == null ? void 0 : node.description) ?? "");
10752
10952
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
10953
+ setMilestoneId((node == null ? void 0 : node.milestone_id) || "");
10753
10954
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
10754
10955
  setCustomProps(extractCustomPropsFromNode(node || {}));
10755
10956
  setReviewStack((node == null ? void 0 : node.reviewStack) || []);
@@ -10762,6 +10963,7 @@ function QuestDetailsPanel({
10762
10963
  custom_date: "",
10763
10964
  next_assignee_id: ""
10764
10965
  });
10966
+ setMilestoneId((node == null ? void 0 : node.milestone_id) || "");
10765
10967
  setHasUnsavedChanges(false);
10766
10968
  }
10767
10969
  }, [node]);
@@ -11060,7 +11262,8 @@ ${noteText}` : noteText : entry.notes,
11060
11262
  reviewStack: currentReviewStack,
11061
11263
  timeline: currentTimeline,
11062
11264
  ...extrasObj,
11063
- version_node: node.version_node
11265
+ version_node: node.version_node,
11266
+ milestone_id: milestoneId || null
11064
11267
  };
11065
11268
  await onSave(dataToSave, keepOpen);
11066
11269
  onDataUpdate(dataToSave);
@@ -11262,7 +11465,51 @@ ${noteText}` : noteText : entry.notes,
11262
11465
  maxLength: "10",
11263
11466
  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"
11264
11467
  }
11265
- ))))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React19.createElement("div", { className: "relative" }, /* @__PURE__ */ React19.createElement(
11468
+ ))))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Milestone"), /* @__PURE__ */ React19.createElement("div", { className: "relative" }, /* @__PURE__ */ React19.createElement(
11469
+ "button",
11470
+ {
11471
+ type: "button",
11472
+ onClick: () => canEdit && setIsMilestoneDropdownOpen(!isMilestoneDropdownOpen),
11473
+ disabled: !canEdit,
11474
+ 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"}`
11475
+ },
11476
+ /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement(FiTarget3, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React19.createElement("span", { className: "text-slate-200 font-medium" }, ((_b = (milestones || []).find((m) => m.id === milestoneId)) == null ? void 0 : _b.name) || "Nenhum")),
11477
+ canEdit && /* @__PURE__ */ React19.createElement(FiChevronDown7, { className: `text-slate-400 transition-transform duration-200 ${isMilestoneDropdownOpen ? "rotate-180" : ""}` })
11478
+ ), isMilestoneDropdownOpen && canEdit && /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "fixed inset-0 z-40", onClick: () => setIsMilestoneDropdownOpen(false) }), /* @__PURE__ */ React19.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__ */ React19.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ React19.createElement(FiSearch7, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ React19.createElement(
11479
+ "input",
11480
+ {
11481
+ type: "text",
11482
+ autoFocus: true,
11483
+ placeholder: "Buscar ou criar milestone...",
11484
+ value: milestoneSearchQuery,
11485
+ onChange: (e) => setMilestoneSearchQuery(e.target.value),
11486
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
11487
+ onClick: (e) => e.stopPropagation()
11488
+ }
11489
+ )), /* @__PURE__ */ React19.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React19.createElement(
11490
+ "li",
11491
+ {
11492
+ onClick: () => handleSelectMilestone(""),
11493
+ 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"}`
11494
+ },
11495
+ "Nenhum"
11496
+ ), filteredMilestones.map((ms) => /* @__PURE__ */ React19.createElement(
11497
+ "li",
11498
+ {
11499
+ key: ms.id,
11500
+ onClick: () => handleSelectMilestone(ms.id),
11501
+ 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"}`
11502
+ },
11503
+ ms.name
11504
+ )), milestoneSearchQuery.trim() && !filteredMilestones.some((ms) => ms.name.toLowerCase() === milestoneSearchQuery.toLowerCase().trim()) && /* @__PURE__ */ React19.createElement(
11505
+ "li",
11506
+ {
11507
+ onClick: () => handleCreateMilestone(milestoneSearchQuery),
11508
+ 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"
11509
+ },
11510
+ /* @__PURE__ */ React19.createElement("span", { className: "text-[10px] uppercase font-bold opacity-60" }, "Criar Novo Milestone"),
11511
+ /* @__PURE__ */ React19.createElement("span", { className: "font-medium" }, '"', milestoneSearchQuery, '"')
11512
+ )))))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Status da Quest"), /* @__PURE__ */ React19.createElement("div", { className: "relative" }, /* @__PURE__ */ React19.createElement(
11266
11513
  "button",
11267
11514
  {
11268
11515
  type: "button",
@@ -11448,7 +11695,7 @@ ${noteText}` : noteText : entry.notes,
11448
11695
  onClick: () => setIsReturnDropdownOpen(!isReturnDropdownOpen),
11449
11696
  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"
11450
11697
  },
11451
- /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement(FiUser3, { className: "text-slate-400", size: 12 }), /* @__PURE__ */ React19.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...")),
11698
+ /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement(FiUser3, { className: "text-slate-400", size: 12 }), /* @__PURE__ */ React19.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...")),
11452
11699
  /* @__PURE__ */ React19.createElement(FiChevronDown7, { className: `text-slate-400 transition-transform duration-200 ${isReturnDropdownOpen ? "rotate-180" : ""}`, size: 14 })
11453
11700
  ), isReturnDropdownOpen && /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
11454
11701
  setIsReturnDropdownOpen(false);
@@ -13129,7 +13376,7 @@ function XViewScene({
13129
13376
  check_user_permission,
13130
13377
  get_view_members
13131
13378
  }) {
13132
- var _a, _b, _c, _d, _e, _f, _g, _h;
13379
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
13133
13380
  const { data: session, status } = useSession();
13134
13381
  const router = useRouter();
13135
13382
  const searchParams = useSearchParams();
@@ -13958,20 +14205,23 @@ function XViewScene({
13958
14205
  sceneResponse.data.scene.nodes = [];
13959
14206
  sceneResponse.data.scene.links = [];
13960
14207
  }
13961
- sceneDataRef.current = sceneResponse.data.scene;
14208
+ sceneDataRef.current = {
14209
+ ...sceneResponse.data.scene,
14210
+ milestones: sceneResponse.data.scene.milestones || []
14211
+ };
13962
14212
  parentDataRef.current = sceneResponse.data.parent;
13963
14213
  ancestryDataRef.current = sceneResponse.data.ancestry;
13964
14214
  console.log(
13965
14215
  "Console de sceneResponse.data.scene:",
13966
- sceneResponse.data.scene
14216
+ sceneDataRef.current
13967
14217
  );
13968
14218
  console.log(
13969
14219
  "Console de sceneResponse.data.parent:",
13970
- sceneResponse.data.parent
14220
+ parentDataRef.current
13971
14221
  );
13972
14222
  console.log(
13973
14223
  "Console de sceneResponse.data.ancestry:",
13974
- sceneResponse.data.ancestry
14224
+ ancestryDataRef.current
13975
14225
  );
13976
14226
  setIsInitialized(true);
13977
14227
  } else {
@@ -14103,6 +14353,9 @@ function XViewScene({
14103
14353
  timeToYMap.set(ms, idx * TIMELINE_LAYER_SPACING_Y);
14104
14354
  });
14105
14355
  const startX = -((QUEST_STATUS_ORDER.length - 1) * QUEST_LENS_COLUMN_SPACING) / 2;
14356
+ let sumX = 0;
14357
+ let sumY = 0;
14358
+ let questCount = 0;
14106
14359
  const positionGroups = /* @__PURE__ */ new Map();
14107
14360
  const NODE_X_GAP = 22;
14108
14361
  QUEST_STATUS_ORDER.forEach((statusName) => {
@@ -14206,6 +14459,9 @@ function XViewScene({
14206
14459
  const lensTween = new Tween2(mesh.position).to({ x: finalX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
14207
14460
  tweenGroup.add(lensTween);
14208
14461
  lensTween.start();
14462
+ sumX += finalX;
14463
+ sumY += targetY;
14464
+ questCount++;
14209
14465
  if (!timeInfo.isUndated && timeInfo.label && !mesh.getObjectByName("questDateLabel")) {
14210
14466
  const dateSprite = createTextSprite(timeInfo.label, 36);
14211
14467
  dateSprite.name = "questDateLabel";
@@ -14254,6 +14510,18 @@ function XViewScene({
14254
14510
  }
14255
14511
  });
14256
14512
  });
14513
+ if (questCount > 0) {
14514
+ const { camera, controls } = stateRef.current;
14515
+ const centerX = sumX / questCount;
14516
+ const centerY = sumY / questCount;
14517
+ const targetFocus = new THREE3.Vector3(centerX, centerY, 0);
14518
+ const targetTween = new Tween2(controls.target).to({ x: targetFocus.x, y: targetFocus.y, z: targetFocus.z }, 1500).easing(Easing2.Cubic.InOut);
14519
+ tweenGroup.add(targetTween);
14520
+ targetTween.start();
14521
+ const camTween = new Tween2(camera.position).to({ x: targetFocus.x, y: targetFocus.y, z: 140 }, 1500).easing(Easing2.Cubic.InOut);
14522
+ tweenGroup.add(camTween);
14523
+ camTween.start();
14524
+ }
14257
14525
  }, [addOrUpdateNodeMesh]);
14258
14526
  useEffect22(() => {
14259
14527
  if (!isInitialized || !sceneDataRef.current) return;
@@ -15476,7 +15744,8 @@ function XViewScene({
15476
15744
  links: sceneDataRef2.current.links,
15477
15745
  quest_nodes: graphDataRef.current[sceneConfigId2].nodes,
15478
15746
  quest_links: graphDataRef.current[sceneConfigId2].links,
15479
- quest_counter: currentCounter + 1
15747
+ quest_counter: currentCounter + 1,
15748
+ milestones: sceneDataRef2.current.milestones || []
15480
15749
  };
15481
15750
  try {
15482
15751
  await actions.save_view_data(sceneSaveUrl2, sceneFileData);
@@ -17128,6 +17397,52 @@ function XViewScene({
17128
17397
  },
17129
17398
  [actionHandlerContext]
17130
17399
  );
17400
+ const handleUpdateMilestones = useCallback4(
17401
+ async (newMilestones) => {
17402
+ var _a2, _b2, _c2;
17403
+ if (!sceneDataRef.current) return;
17404
+ sceneDataRef.current.milestones = newMilestones;
17405
+ setSceneVersion((v) => v + 1);
17406
+ const isView = ((_a2 = viewParams == null ? void 0 : viewParams.type) == null ? void 0 : _a2.toLowerCase()) === "view";
17407
+ if (!isView || !save_view_data || !sceneSaveUrl) return;
17408
+ const { nodeObjects, allLinks } = stateRef.current;
17409
+ const currentNodes = Object.values(nodeObjects || {}).map((mesh) => {
17410
+ const {
17411
+ _baseEmissiveIntensity,
17412
+ labelObject,
17413
+ labelOffset,
17414
+ timelineIntervalBar,
17415
+ timelineEndLabel,
17416
+ ...serializableNodeData
17417
+ } = mesh.userData;
17418
+ return serializableNodeData;
17419
+ });
17420
+ const currentLinks = (allLinks || []).map((line) => {
17421
+ const { sourceNode, targetNode, ...serializableLinkData } = line.userData;
17422
+ return serializableLinkData;
17423
+ });
17424
+ const sceneFileData = {
17425
+ parent_dbs: sceneDataRef.current.parent_dbs,
17426
+ nodes: currentNodes,
17427
+ links: currentLinks,
17428
+ quest_nodes: ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || sceneDataRef.current.quest_nodes || [],
17429
+ quest_links: ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || sceneDataRef.current.quest_links || [],
17430
+ quest_counter: sceneDataRef.current.quest_counter || 1,
17431
+ milestones: newMilestones
17432
+ };
17433
+ try {
17434
+ await save_view_data(sceneSaveUrl, sceneFileData);
17435
+ } catch (error) {
17436
+ console.error("Erro ao salvar milestones:", error);
17437
+ }
17438
+ },
17439
+ [
17440
+ sceneConfigId,
17441
+ sceneSaveUrl,
17442
+ save_view_data,
17443
+ viewParams == null ? void 0 : viewParams.type
17444
+ ]
17445
+ );
17131
17446
  const handleSaveCurrentView = useCallback4(async () => {
17132
17447
  var _a2, _b2, _c2;
17133
17448
  const { nodeObjects, allLinks } = stateRef.current;
@@ -17162,7 +17477,8 @@ function XViewScene({
17162
17477
  links: currentLinks,
17163
17478
  quest_nodes: isView ? ((_b2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _b2.nodes) || [] : sceneDataRef.current.quest_nodes || [],
17164
17479
  quest_links: isView ? ((_c2 = parentDataRef.current[sceneConfigId]) == null ? void 0 : _c2.links) || [] : sceneDataRef.current.quest_links || [],
17165
- quest_counter: sceneDataRef.current.quest_counter || 1
17480
+ quest_counter: sceneDataRef.current.quest_counter || 1,
17481
+ milestones: sceneDataRef.current.milestones || []
17166
17482
  };
17167
17483
  try {
17168
17484
  await save_view_data(sceneSaveUrl, sceneFileData);
@@ -17499,7 +17815,9 @@ function XViewScene({
17499
17815
  isOpen: isSidebarOpen,
17500
17816
  onToggle: setIsSidebarOpen,
17501
17817
  userRole: userPermissionRole,
17502
- viewMembers
17818
+ viewMembers,
17819
+ milestones: ((_c = sceneDataRef.current) == null ? void 0 : _c.milestones) || [],
17820
+ onUpdateMilestones: handleUpdateMilestones
17503
17821
  }
17504
17822
  ),
17505
17823
  creationMode.isActive && /* @__PURE__ */ React27.createElement(
@@ -17523,8 +17841,8 @@ function XViewScene({
17523
17841
  },
17524
17842
  refEl: formRef,
17525
17843
  existingTypes: existingNodeTypes,
17526
- initialColor: (_c = creationMode.sourceNodeData) == null ? void 0 : _c.color,
17527
- sourceTypes: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.type,
17844
+ initialColor: (_d = creationMode.sourceNodeData) == null ? void 0 : _d.color,
17845
+ sourceTypes: (_e = creationMode.sourceNodeData) == null ? void 0 : _e.type,
17528
17846
  onIntensityChange: handleGhostNodeIntensityChange,
17529
17847
  onUploadFile: upload_file_action,
17530
17848
  availableDatasets,
@@ -17553,8 +17871,8 @@ function XViewScene({
17553
17871
  transition: "opacity 200ms ease-out"
17554
17872
  },
17555
17873
  refEl: formRef,
17556
- fixedType: (_e = versionMode.sourceNodeData) == null ? void 0 : _e.type,
17557
- fixedColor: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.color,
17874
+ fixedType: (_f = versionMode.sourceNodeData) == null ? void 0 : _f.type,
17875
+ fixedColor: (_g = versionMode.sourceNodeData) == null ? void 0 : _g.color,
17558
17876
  onUploadFile: upload_file_action,
17559
17877
  availableNodes: allAvailableNodes,
17560
17878
  availableAncestries: allAvailableAncestries
@@ -17582,8 +17900,10 @@ function XViewScene({
17582
17900
  availableNodes: allAvailableNodes,
17583
17901
  availableAncestries: allAvailableAncestries,
17584
17902
  viewName: viewParams == null ? void 0 : viewParams.name,
17585
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
17586
- viewMembers
17903
+ questCounter: ((_h = sceneDataRef.current) == null ? void 0 : _h.quest_counter) || 1,
17904
+ viewMembers,
17905
+ milestones: ((_i = sceneDataRef.current) == null ? void 0 : _i.milestones) || [],
17906
+ onUpdateMilestones: handleUpdateMilestones
17587
17907
  }
17588
17908
  ),
17589
17909
  readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ React27.createElement(
@@ -17715,7 +18035,9 @@ function XViewScene({
17715
18035
  userRole: userPermissionRole,
17716
18036
  currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
17717
18037
  viewMembers,
17718
- currentUser: session == null ? void 0 : session.user
18038
+ currentUser: session == null ? void 0 : session.user,
18039
+ milestones: ((_j = sceneDataRef.current) == null ? void 0 : _j.milestones) || [],
18040
+ onUpdateMilestones: handleUpdateMilestones
17719
18041
  }
17720
18042
  ),
17721
18043
  detailsNode && !detailsNode.is_quest && /* @__PURE__ */ React27.createElement(
@@ -17900,7 +18222,7 @@ function XViewScene({
17900
18222
  onClose: () => setIsImportModalOpen(false),
17901
18223
  onConfirm: handleConfirmImport,
17902
18224
  session,
17903
- parentDbs: ((_h = sceneDataRef.current) == null ? void 0 : _h.parent_dbs) || [],
18225
+ parentDbs: ((_k = sceneDataRef.current) == null ? void 0 : _k.parent_dbs) || [],
17904
18226
  onFetchAvailableFiles: import_parent_file_modal_get,
17905
18227
  currentViewName: viewParams == null ? void 0 : viewParams.name,
17906
18228
  currentAncestries: ancestryDataRef.current || []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.5-dev.45",
3
+ "version": "1.2.5-dev.47",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",