@lv-x-software-house/x_view 1.2.5-dev.2 → 1.2.5-dev.21

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 +850 -156
  2. package/dist/index.mjs +854 -161
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -103,7 +103,10 @@ function ContextMenu({
103
103
  onEditAncestry,
104
104
  onDeleteAncestry,
105
105
  onFocusNode,
106
- onClose
106
+ onClose,
107
+ viewMembers,
108
+ currentUser,
109
+ onStartQuest
107
110
  }) {
108
111
  var _a, _b, _c;
109
112
  const menuRef = useRef(null);
@@ -111,6 +114,7 @@ function ContextMenu({
111
114
  const [menuView, setMenuView] = useState("main");
112
115
  const [selectedAncestry, setSelectedAncestry] = useState(null);
113
116
  const [versionSubMenu, setVersionSubMenu] = useState(null);
117
+ const [labelSubMenu, setLabelSubMenu] = useState(null);
114
118
  const [isLinkCopied, setIsLinkCopied] = useState(false);
115
119
  const [selectedQuestStatus, setSelectedQuestStatus] = useState(null);
116
120
  const ability = useMemo(() => defineAbilityFor(userRole), [userRole]);
@@ -119,6 +123,7 @@ function ContextMenu({
119
123
  setMenuView("main");
120
124
  setSelectedAncestry(null);
121
125
  setVersionSubMenu(null);
126
+ setLabelSubMenu(null);
122
127
  setSelectedQuestStatus(null);
123
128
  }
124
129
  }, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
@@ -134,7 +139,7 @@ function ContextMenu({
134
139
  if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
135
140
  if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
136
141
  setMenuPos({ left, top });
137
- }, [data, menuView, versionSubMenu, selectedQuestStatus]);
142
+ }, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
138
143
  useEffect(() => {
139
144
  if (!data.visible) return;
140
145
  const handleClickOutside = (e) => {
@@ -186,7 +191,21 @@ function ContextMenu({
186
191
  var _a2;
187
192
  return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
188
193
  });
189
- const groupedConnections = commonConnections.reduce((acc, conn) => {
194
+ const getLabelForConnection = (conn) => {
195
+ if (conn.direction === "outgoing") return conn.link.source_label || null;
196
+ if (conn.direction === "incoming") return conn.link.target_label || null;
197
+ return null;
198
+ };
199
+ const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
200
+ const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
201
+ const labelGroups = commonWithLabel.reduce((acc, conn) => {
202
+ const label = getLabelForConnection(conn);
203
+ if (!acc[label]) acc[label] = [];
204
+ acc[label].push(conn);
205
+ return acc;
206
+ }, {});
207
+ const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
208
+ const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
190
209
  var _a2;
191
210
  const { targetNode } = conn;
192
211
  const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
@@ -288,12 +307,25 @@ function ContextMenu({
288
307
  });
289
308
  };
290
309
  const renderMainView = () => {
291
- var _a2;
310
+ var _a2, _b2, _c2;
292
311
  const hasVersions = computedVersions.length > 0;
293
312
  const canCreateVersion = ability.can("create", "Versioning");
294
313
  const canReadVersion = ability.can("read", "Versioning");
295
314
  const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
296
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_a2 = data.nodeData) == null ? void 0 : _a2.is_quest) && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ React.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__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ React.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__ */ React.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => {
315
+ const canStartQuest = isCurrentNodeQuest && (((_a2 = data.nodeData) == null ? void 0 : _a2.status) !== "In Progress" || ((_b2 = data.nodeData) == null ? void 0 : _b2.assignee_id) !== (currentUser == null ? void 0 : currentUser.id));
316
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, canStartQuest && /* @__PURE__ */ React.createElement(
317
+ "button",
318
+ {
319
+ onClick: () => {
320
+ onStartQuest == null ? void 0 : onStartQuest(data.nodeData);
321
+ onClose();
322
+ },
323
+ className: `w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md bg-yellow-500/10 text-yellow-400 hover:bg-yellow-500/20 hover:text-yellow-300 transition-colors duration-150 truncate font-semibold shadow-inner`,
324
+ title: "Atribuir a mim e iniciar"
325
+ },
326
+ /* @__PURE__ */ React.createElement("span", null, "\u{1F680}"),
327
+ /* @__PURE__ */ React.createElement("span", null, "Iniciar Quest")
328
+ ), ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_c2 = data.nodeData) == null ? void 0 : _c2.is_quest) && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ React.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__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ React.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__ */ React.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => {
297
329
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
298
330
  onClose();
299
331
  }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ React.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__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ React.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ React.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ React.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ React.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss")), /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ React.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__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ React.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ React.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ React.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__ */ React.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ React.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__ */ React.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ React.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ React.createElement("span", null, "Excluir Node"))));
@@ -314,11 +346,32 @@ function ContextMenu({
314
346
  /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
315
347
  ))));
316
348
  };
349
+ const renderLabelSubMenuView = () => {
350
+ const group = labelSubMenu;
351
+ const isScrollable = group.connections.length > 10;
352
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => {
353
+ setLabelSubMenu(null);
354
+ setMenuView("connections");
355
+ }, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React.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", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ React.createElement(
356
+ "button",
357
+ {
358
+ key: conn.targetNode.id,
359
+ onClick: () => handleExpandAndClose([conn.link]),
360
+ className: baseButtonClass,
361
+ title: `Expandir conex\xE3o com ${conn.targetNode.name}`
362
+ },
363
+ /* @__PURE__ */ React.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__ */ React.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ React.createElement("polyline", { points: "12 5 19 12 12 19" })),
364
+ /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
365
+ ))));
366
+ };
317
367
  const renderConnectionsView = () => {
318
368
  if (versionSubMenu) {
319
369
  return renderVersionSubMenuView();
320
370
  }
321
- const totalItems = availableAncestries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
371
+ if (labelSubMenu) {
372
+ return renderLabelSubMenuView();
373
+ }
374
+ const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
322
375
  const isScrollable = totalItems > 10;
323
376
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ React.createElement(
324
377
  "button",
@@ -330,7 +383,20 @@ function ContextMenu({
330
383
  },
331
384
  /* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "21", r: "2" })),
332
385
  /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
333
- ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
386
+ ))), labelGroupEntries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ React.createElement(
387
+ "button",
388
+ {
389
+ key: label,
390
+ onClick: () => {
391
+ setLabelSubMenu({ label, connections: conns });
392
+ },
393
+ className: baseButtonClass,
394
+ title: `Ver grupo: ${label}`
395
+ },
396
+ /* @__PURE__ */ React.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", className: "text-violet-400" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
397
+ /* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, label),
398
+ /* @__PURE__ */ React.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
399
+ ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
334
400
  if (group.isVersionGroup) {
335
401
  return /* @__PURE__ */ React.createElement(
336
402
  "button",
@@ -514,15 +580,18 @@ function XViewSidebar({
514
580
  const [isFilterMenuOpen, setIsFilterMenuOpen] = useState2(false);
515
581
  const [filterCategory, setFilterCategory] = useState2("Type");
516
582
  const [filterValue, setFilterValue] = useState2("");
583
+ const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState2(false);
584
+ const [highlightedTypeIndex, setHighlightedTypeIndex] = useState2(-1);
517
585
  const containerRef = useRef2(null);
518
586
  const inputRef = useRef2(null);
519
587
  const filterMenuRef = useRef2(null);
588
+ const typeDropdownRef = useRef2(null);
520
589
  const ability = useMemo2(() => {
521
590
  return defineAbilityFor(userRole);
522
591
  }, [userRole]);
523
592
  const contextLabel = useMemo2(() => {
524
593
  if (!viewType || !viewName) return null;
525
- const typeLower = viewType.toLowerCase();
594
+ const typeLower = String(viewType).toLowerCase();
526
595
  if (typeLower === "database") {
527
596
  return `Dataset: ${viewName}`;
528
597
  } else if (typeLower === "view") {
@@ -532,7 +601,12 @@ function XViewSidebar({
532
601
  }
533
602
  return `${viewType}: ${viewName}`;
534
603
  }, [viewType, viewName]);
535
- const normalize = (str = "") => String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
604
+ const normalize = (str = "") => {
605
+ if (str === void 0 || str === null) {
606
+ console.warn("XViewSidebar: normalize recebeu valor nulo/indefinido:", str);
607
+ }
608
+ return String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
609
+ };
536
610
  const collator = useMemo2(
537
611
  () => new Intl.Collator("pt-BR", { sensitivity: "base", numeric: true }),
538
612
  []
@@ -541,13 +615,30 @@ function XViewSidebar({
541
615
  const typesSet = /* @__PURE__ */ new Set();
542
616
  (dbNodes || []).forEach((node) => {
543
617
  if (Array.isArray(node.type)) {
544
- node.type.forEach((t) => typesSet.add(t));
545
- } else if (node.type) {
618
+ node.type.forEach((t) => {
619
+ if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
620
+ });
621
+ } else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
546
622
  typesSet.add(node.type);
547
623
  }
548
624
  });
549
625
  return Array.from(typesSet).sort();
550
626
  }, [dbNodes]);
627
+ const availableDatasets = useMemo2(() => {
628
+ const datasetsSet = /* @__PURE__ */ new Set();
629
+ (dbNodes || []).forEach((node) => {
630
+ if (node.dataset_name) {
631
+ datasetsSet.add(node.dataset_name);
632
+ }
633
+ });
634
+ return Array.from(datasetsSet).sort();
635
+ }, [dbNodes]);
636
+ const filteredAutocompleteOptions = useMemo2(() => {
637
+ const source = filterCategory === "Dataset" ? availableDatasets : availableTypes;
638
+ if (!filterValue.trim()) return source;
639
+ const search = filterValue.toLowerCase().trim();
640
+ return source.filter((item) => item.toLowerCase().includes(search));
641
+ }, [availableTypes, availableDatasets, filterValue, filterCategory]);
551
642
  const filteredAndSorted = useMemo2(() => {
552
643
  const base = Array.isArray(dbNodes) ? dbNodes : [];
553
644
  let pool = base;
@@ -567,6 +658,8 @@ function XViewSidebar({
567
658
  return nodeTypes.some((t) => String(t).toLowerCase() === String(filter.value).toLowerCase());
568
659
  } else if (filter.type === "Color") {
569
660
  return String(node.color).toLowerCase() === String(filter.value).toLowerCase();
661
+ } else if (filter.type === "Dataset") {
662
+ return String(node.dataset_name || "").toLowerCase() === String(filter.value).toLowerCase();
570
663
  }
571
664
  return true;
572
665
  });
@@ -582,18 +675,21 @@ function XViewSidebar({
582
675
  inView.sort(byName);
583
676
  const ordered = [...notInView, ...inView];
584
677
  return ordered.slice(0, 200);
585
- }, [dbNodes, query, isNodeInView, viewVersion, activeFilters]);
678
+ }, [dbNodes, query, isNodeInView, viewVersion, activeFilters, filterCategory]);
586
679
  useEffect2(() => {
587
680
  const handleClickOutside = (event) => {
588
681
  if (filterMenuRef.current && !filterMenuRef.current.contains(event.target)) {
589
682
  setIsFilterMenuOpen(false);
590
683
  }
684
+ if (typeDropdownRef.current && !typeDropdownRef.current.contains(event.target)) {
685
+ setIsTypeDropdownOpen(false);
686
+ }
591
687
  };
592
- if (isFilterMenuOpen) {
688
+ if (isFilterMenuOpen || isTypeDropdownOpen) {
593
689
  document.addEventListener("mousedown", handleClickOutside);
594
690
  }
595
691
  return () => document.removeEventListener("mousedown", handleClickOutside);
596
- }, [isFilterMenuOpen]);
692
+ }, [isFilterMenuOpen, isTypeDropdownOpen]);
597
693
  useEffect2(() => {
598
694
  if (!query) setSelectedNodeId(null);
599
695
  }, [query]);
@@ -647,6 +743,35 @@ function XViewSidebar({
647
743
  const handleRemoveFilter = (index) => {
648
744
  setActiveFilters((prev) => prev.filter((_, i) => i !== index));
649
745
  };
746
+ const handleTypeKeyDown = (e) => {
747
+ if (!isTypeDropdownOpen) {
748
+ setIsTypeDropdownOpen(true);
749
+ }
750
+ if (e.key === "ArrowDown") {
751
+ e.preventDefault();
752
+ setHighlightedTypeIndex(
753
+ (prev) => prev < filteredAutocompleteOptions.length - 1 ? prev + 1 : prev
754
+ );
755
+ } else if (e.key === "ArrowUp") {
756
+ e.preventDefault();
757
+ setHighlightedTypeIndex((prev) => prev > 0 ? prev - 1 : 0);
758
+ } else if (e.key === "Enter") {
759
+ e.preventDefault();
760
+ if (highlightedTypeIndex >= 0 && filteredAutocompleteOptions[highlightedTypeIndex]) {
761
+ setFilterValue(filteredAutocompleteOptions[highlightedTypeIndex]);
762
+ setIsTypeDropdownOpen(false);
763
+ setHighlightedTypeIndex(-1);
764
+ } else {
765
+ handleAddFilter();
766
+ }
767
+ } else if (e.key === "Escape") {
768
+ e.preventDefault();
769
+ setIsTypeDropdownOpen(false);
770
+ setHighlightedTypeIndex(-1);
771
+ } else if (e.key === "Tab") {
772
+ setIsTypeDropdownOpen(false);
773
+ }
774
+ };
650
775
  const ToggleButton = /* @__PURE__ */ React2.createElement(
651
776
  "button",
652
777
  {
@@ -666,7 +791,7 @@ function XViewSidebar({
666
791
  "div",
667
792
  {
668
793
  ref: containerRef,
669
- className: "ui-overlay fixed left-0 top-0 h-full w-[min(92vw,320px)] z-40",
794
+ className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
670
795
  onPointerDown: swallow,
671
796
  onPointerMove: swallow,
672
797
  onPointerUp: swallow,
@@ -675,7 +800,7 @@ function XViewSidebar({
675
800
  onContextMenu: swallow,
676
801
  onDoubleClick: swallow
677
802
  },
678
- /* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
803
+ /* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
679
804
  "button",
680
805
  {
681
806
  className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
@@ -732,7 +857,7 @@ function XViewSidebar({
732
857
  autoComplete: "off"
733
858
  }
734
859
  )
735
- ), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[28rem] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
860
+ ), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
736
861
  const inView = isNodeInView(n.id);
737
862
  const active = selectedNodeId === n.id;
738
863
  const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
@@ -766,7 +891,7 @@ function XViewSidebar({
766
891
  },
767
892
  /* @__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("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })),
768
893
  "Filters"
769
- ), isFilterMenuOpen && /* @__PURE__ */ React2.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ React2.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color"].map((opt) => /* @__PURE__ */ React2.createElement(
894
+ ), isFilterMenuOpen && /* @__PURE__ */ React2.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ React2.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ React2.createElement(
770
895
  "button",
771
896
  {
772
897
  key: opt,
@@ -777,18 +902,36 @@ function XViewSidebar({
777
902
  className: `flex-1 text-xs py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white shadow-sm" : "text-slate-400 hover:text-slate-200"}`
778
903
  },
779
904
  opt
780
- )))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" ? /* @__PURE__ */ React2.createElement("div", { className: "relative" }, /* @__PURE__ */ React2.createElement(
905
+ )))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" || filterCategory === "Dataset" ? /* @__PURE__ */ React2.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ React2.createElement(
781
906
  "input",
782
907
  {
783
- list: "typeOptions",
784
908
  type: "text",
785
909
  value: filterValue,
786
- onChange: (e) => setFilterValue(e.target.value),
787
- placeholder: "Ex: Concept",
910
+ onChange: (e) => {
911
+ setFilterValue(e.target.value);
912
+ if (!isTypeDropdownOpen) setIsTypeDropdownOpen(true);
913
+ setHighlightedTypeIndex(-1);
914
+ },
915
+ onFocus: () => setIsTypeDropdownOpen(true),
916
+ onKeyDown: handleTypeKeyDown,
917
+ placeholder: filterCategory === "Type" ? "Ex: Concept" : "Ex: Dataset 1",
788
918
  className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white",
789
919
  autoFocus: true
790
920
  }
791
- ), /* @__PURE__ */ React2.createElement("datalist", { id: "typeOptions" }, availableTypes.map((t) => /* @__PURE__ */ React2.createElement("option", { key: t, value: t })))) : /* @__PURE__ */ React2.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ React2.createElement(
921
+ ), isTypeDropdownOpen && filteredAutocompleteOptions.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "absolute z-[60] left-0 right-0 mt-1 max-h-48 overflow-y-auto bg-slate-900/95 backdrop-blur-xl border border-white/10 rounded-md shadow-2xl custom-scrollbar" }, filteredAutocompleteOptions.map((t, idx) => /* @__PURE__ */ React2.createElement(
922
+ "div",
923
+ {
924
+ key: t,
925
+ className: `px-3 py-2 text-xs cursor-pointer transition-colors ${idx === highlightedTypeIndex ? "bg-indigo-600 text-white" : "text-slate-300 hover:bg-white/10"}`,
926
+ onMouseDown: (e) => {
927
+ e.preventDefault();
928
+ setFilterValue(t);
929
+ setIsTypeDropdownOpen(false);
930
+ setHighlightedTypeIndex(-1);
931
+ }
932
+ },
933
+ t
934
+ )))) : /* @__PURE__ */ React2.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ React2.createElement(
792
935
  "div",
793
936
  {
794
937
  className: "absolute left-2 w-3 h-3 rounded-full border border-white/20 shadow-sm",
@@ -2587,7 +2730,15 @@ var userActionHandlers = {
2587
2730
  var _a;
2588
2731
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2589
2732
  if (!graphDataRef.current || !sceneDataRef.current) return;
2590
- const { _baseEmissiveIntensity: ignored, ...nodeToSave } = updatedNode;
2733
+ const {
2734
+ _baseEmissiveIntensity,
2735
+ _baseScale,
2736
+ labelObject,
2737
+ labelOffset,
2738
+ timelineIntervalBar,
2739
+ timelineEndLabel,
2740
+ ...nodeToSave
2741
+ } = updatedNode;
2591
2742
  const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id, context.sceneConfigId, context.ownerId);
2592
2743
  if (!parentInfo || !parentInfo.ownerId) {
2593
2744
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node a ser atualizado:", nodeToSave.id);
@@ -2776,18 +2927,27 @@ function createNewCustomProperty(existingProps = []) {
2776
2927
  };
2777
2928
  }
2778
2929
  var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
2779
- const match = refString.match(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/);
2930
+ const match = refString.match(
2931
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
2932
+ );
2780
2933
  if (!match) return null;
2781
2934
  const [_, type, itemId, sectionId] = match;
2782
2935
  let sourceItem = null;
2783
2936
  if (type === "node") {
2784
2937
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
2785
2938
  } else {
2786
- sourceItem = availableAncestries.find((a) => String(a.ancestry_id) === String(itemId));
2939
+ sourceItem = availableAncestries.find(
2940
+ (a) => String(a.ancestry_id) === String(itemId)
2941
+ );
2787
2942
  }
2788
2943
  if (!sourceItem) return null;
2789
- const sections = parseDescriptionSections(sourceItem.description, sourceItem.description_sections);
2790
- const targetSection = sections.find((s) => String(s.id) === String(sectionId));
2944
+ const sections = parseDescriptionSections(
2945
+ sourceItem.description,
2946
+ sourceItem.description_sections
2947
+ );
2948
+ const targetSection = sections.find(
2949
+ (s) => String(s.id) === String(sectionId)
2950
+ );
2791
2951
  if (!targetSection) return null;
2792
2952
  return {
2793
2953
  content: targetSection.content,
@@ -2801,21 +2961,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2801
2961
  let text = rawText;
2802
2962
  const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
2803
2963
  const allAncestries = ancestryData || [];
2804
- text = text.replace(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g, (match, type, itemId, sectionId) => {
2805
- const resolved = resolveDescriptionReference(match, allNodes, allAncestries);
2806
- if (resolved && !resolved.error) {
2807
- return resolved.content;
2964
+ text = text.replace(
2965
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
2966
+ (match, type, itemId, sectionId) => {
2967
+ const resolved = resolveDescriptionReference(
2968
+ match,
2969
+ allNodes,
2970
+ allAncestries
2971
+ );
2972
+ if (resolved && !resolved.error) {
2973
+ return resolved.content;
2974
+ }
2975
+ return "[Refer\xEAncia indispon\xEDvel]";
2808
2976
  }
2809
- return "[Refer\xEAncia indispon\xEDvel]";
2810
- });
2977
+ );
2811
2978
  text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
2812
- text = text.replace(/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g, (match, nodeId) => {
2813
- const node = allNodes.find((n) => String(n.id) === String(nodeId));
2814
- return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2815
- });
2816
- text = text.replace(/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g, (match, url, name) => {
2817
- return name ? `[Imagem: ${name}]` : `[Imagem]`;
2818
- });
2979
+ text = text.replace(
2980
+ /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
2981
+ (match, nodeId) => {
2982
+ const node = allNodes.find((n) => String(n.id) === String(nodeId));
2983
+ return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2984
+ }
2985
+ );
2986
+ text = text.replace(
2987
+ /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
2988
+ (match, url, name) => {
2989
+ return name ? `[Imagem: ${name}]` : `[Imagem]`;
2990
+ }
2991
+ );
2819
2992
  text = text.replace(/^#+\s*/gm, "");
2820
2993
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
2821
2994
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
@@ -2823,7 +2996,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2823
2996
  return text.trim();
2824
2997
  }
2825
2998
  function generateTooltipHtml(data, parentData, ancestryData) {
2826
- const ignoredKeys = ["id", "name", "type", "color", "_baseEmissiveIntensity", "description"];
2999
+ const ignoredKeys = [
3000
+ "id",
3001
+ "name",
3002
+ "type",
3003
+ "color",
3004
+ "_baseEmissiveIntensity",
3005
+ "description"
3006
+ ];
2827
3007
  const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
2828
3008
  const extras = customKeys.length;
2829
3009
  let typeDisplay = "Node";
@@ -2838,7 +3018,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
2838
3018
  <div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
2839
3019
  <div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
2840
3020
  if (data.description) {
2841
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3021
+ const cleanDesc = formatDescriptionForTooltip(
3022
+ data.description,
3023
+ parentData,
3024
+ ancestryData
3025
+ );
2842
3026
  if (cleanDesc) {
2843
3027
  html += `<div style="
2844
3028
  margin-top: 6px;
@@ -2876,7 +3060,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
2876
3060
  html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
2877
3061
  }
2878
3062
  if (hasDescription) {
2879
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3063
+ const cleanDesc = formatDescriptionForTooltip(
3064
+ data.description,
3065
+ parentData,
3066
+ ancestryData
3067
+ );
2880
3068
  if (cleanDesc) {
2881
3069
  html += `<div style="
2882
3070
  display: -webkit-box;
@@ -2958,7 +3146,8 @@ var IGNORED_KEYS = [
2958
3146
  "isAddingAbstractionNodes",
2959
3147
  "status",
2960
3148
  "is_quest",
2961
- "raw_title"
3149
+ "raw_title",
3150
+ "assignee_id"
2962
3151
  ];
2963
3152
  function extractCustomPropsFromNode(node) {
2964
3153
  const customPropTypes = node._customPropTypes || {};
@@ -2968,16 +3157,40 @@ function extractCustomPropsFromNode(node) {
2968
3157
  if (t === "date") {
2969
3158
  if (val && typeof val === "object") {
2970
3159
  if ("date_interval" in val) {
2971
- return { id: v4_default(), key, type: "date", value: { type: "Date Interval", start: val.date_interval.start || "", end: val.date_interval.end || "" } };
3160
+ return {
3161
+ id: v4_default(),
3162
+ key,
3163
+ type: "date",
3164
+ value: {
3165
+ type: "Date Interval",
3166
+ start: val.date_interval.start || "",
3167
+ end: val.date_interval.end || ""
3168
+ }
3169
+ };
2972
3170
  }
2973
3171
  if ("year" in val) {
2974
- return { id: v4_default(), key, type: "date", value: { type: "Ano", value: val.year || "" } };
3172
+ return {
3173
+ id: v4_default(),
3174
+ key,
3175
+ type: "date",
3176
+ value: { type: "Ano", value: val.year || "" }
3177
+ };
2975
3178
  }
2976
3179
  if ("date" in val) {
2977
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: val.date || "" } };
3180
+ return {
3181
+ id: v4_default(),
3182
+ key,
3183
+ type: "date",
3184
+ value: { type: "Data", value: val.date || "" }
3185
+ };
2978
3186
  }
2979
3187
  }
2980
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: "" } };
3188
+ return {
3189
+ id: v4_default(),
3190
+ key,
3191
+ type: "date",
3192
+ value: { type: "Data", value: "" }
3193
+ };
2981
3194
  }
2982
3195
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
2983
3196
  const safeList = (val || []).map((item) => ({
@@ -2987,7 +3200,12 @@ function extractCustomPropsFromNode(node) {
2987
3200
  }));
2988
3201
  return { id: v4_default(), key, type: t, value: safeList };
2989
3202
  }
2990
- return { id: v4_default(), key, type: t, value: t === "number" ? Number(val) || 0 : String(val ?? "") };
3203
+ return {
3204
+ id: v4_default(),
3205
+ key,
3206
+ type: t,
3207
+ value: t === "number" ? Number(val) || 0 : String(val ?? "")
3208
+ };
2991
3209
  });
2992
3210
  }
2993
3211
  function toObjectFromCustomProps(customProps) {
@@ -3002,7 +3220,12 @@ function toObjectFromCustomProps(customProps) {
3002
3220
  const { type: uiDateType, ...dateValues } = p.value || {};
3003
3221
  if (uiDateType) {
3004
3222
  if (uiDateType === "Date Interval") {
3005
- out[key] = { date_interval: { start: dateValues.start || "", end: dateValues.end || "" } };
3223
+ out[key] = {
3224
+ date_interval: {
3225
+ start: dateValues.start || "",
3226
+ end: dateValues.end || ""
3227
+ }
3228
+ };
3006
3229
  } else if (uiDateType === "Ano") {
3007
3230
  out[key] = { year: dateValues.value || "" };
3008
3231
  } else {
@@ -3038,10 +3261,20 @@ function createTextSprite(text, fontSize = 64) {
3038
3261
  context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
3039
3262
  const texture = new THREE2.CanvasTexture(canvas);
3040
3263
  texture.colorSpace = THREE2.SRGBColorSpace;
3041
- const spriteMaterial = new THREE2.SpriteMaterial({ map: texture, sizeAttenuation: true, depthWrite: false, transparent: true, toneMapped: false });
3264
+ const spriteMaterial = new THREE2.SpriteMaterial({
3265
+ map: texture,
3266
+ sizeAttenuation: true,
3267
+ depthWrite: false,
3268
+ transparent: true,
3269
+ toneMapped: false
3270
+ });
3042
3271
  const sprite = new THREE2.Sprite(spriteMaterial);
3043
3272
  const scaleFactor = 0.05;
3044
- sprite.scale.set(canvas.width * scaleFactor, canvas.height * scaleFactor, 1);
3273
+ sprite.scale.set(
3274
+ canvas.width * scaleFactor,
3275
+ canvas.height * scaleFactor,
3276
+ 1
3277
+ );
3045
3278
  sprite.layers.set(DEFAULT_LAYER);
3046
3279
  return sprite;
3047
3280
  }
@@ -3057,7 +3290,14 @@ function makeGlowTexture() {
3057
3290
  const canvas = document.createElement("canvas");
3058
3291
  canvas.width = canvas.height = size;
3059
3292
  const ctx = canvas.getContext("2d");
3060
- const grd = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
3293
+ const grd = ctx.createRadialGradient(
3294
+ size / 2,
3295
+ size / 2,
3296
+ 0,
3297
+ size / 2,
3298
+ size / 2,
3299
+ size / 2
3300
+ );
3061
3301
  grd.addColorStop(0, "rgba(255,255,255,1)");
3062
3302
  grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
3063
3303
  grd.addColorStop(1, "rgba(255,255,255,0)");
@@ -3073,8 +3313,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
3073
3313
  let midBoost = 1 - Math.abs(L - 0.5) * 2;
3074
3314
  midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
3075
3315
  const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
3076
- const auraColor = new THREE2.Color(hexColor).lerp(new THREE2.Color("#ffffff"), 0.25);
3077
- const auraMat = new THREE2.SpriteMaterial({ map: glowTexture, color: auraColor, opacity: auraOpacity, blending: THREE2.AdditiveBlending, transparent: true, depthWrite: false, toneMapped: false });
3316
+ const auraColor = new THREE2.Color(hexColor).lerp(
3317
+ new THREE2.Color("#ffffff"),
3318
+ 0.25
3319
+ );
3320
+ const auraMat = new THREE2.SpriteMaterial({
3321
+ map: glowTexture,
3322
+ color: auraColor,
3323
+ opacity: auraOpacity,
3324
+ blending: THREE2.AdditiveBlending,
3325
+ transparent: true,
3326
+ depthWrite: false,
3327
+ toneMapped: false
3328
+ });
3078
3329
  const aura = new THREE2.Sprite(auraMat);
3079
3330
  aura.scale.setScalar(auraScale);
3080
3331
  aura.name = "aura";
@@ -3107,7 +3358,17 @@ function calculateNodePositions(nodes) {
3107
3358
  }
3108
3359
  return positions;
3109
3360
  }
3110
- function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3361
+ function updateTooltip({
3362
+ tooltipEl,
3363
+ hoveredNode,
3364
+ hoveredLink,
3365
+ camera,
3366
+ mountEl,
3367
+ isSceneBusy,
3368
+ parentData,
3369
+ ancestryData
3370
+ }) {
3371
+ var _a, _b;
3111
3372
  if (!tooltipEl || !camera || !mountEl) return;
3112
3373
  let content = "";
3113
3374
  let positionTarget = null;
@@ -3117,20 +3378,35 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3117
3378
  currentId = `node_${hoveredNode.userData.id}`;
3118
3379
  positionTarget = hoveredNode;
3119
3380
  if (tooltipEl.dataset.currentId !== currentId) {
3120
- content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3381
+ content = generateTooltipHtml(
3382
+ hoveredNode.userData,
3383
+ parentData,
3384
+ ancestryData
3385
+ );
3121
3386
  }
3122
3387
  } else if (hoveredLink && !isSceneBusy) {
3123
- currentId = `link_${hoveredLink.userData.id}`;
3124
- if (hoveredLink.userData.isCurved) {
3125
- const positions = hoveredLink.geometry.attributes.position.array;
3126
- const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3127
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3128
- } else {
3129
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3130
- }
3131
- isLink = true;
3132
- if (tooltipEl.dataset.currentId !== currentId) {
3133
- content = hoveredLink.userData.isAncestryLink ? generateLinkTooltipHtml(hoveredLink.userData.relationship || {}, parentData, ancestryData) : generateLinkTooltipHtml(hoveredLink.userData, parentData, ancestryData);
3388
+ const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
3389
+ const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
3390
+ if (hasContent) {
3391
+ currentId = `link_${hoveredLink.userData.id}`;
3392
+ if (hoveredLink.userData.isCurved) {
3393
+ const positions = hoveredLink.geometry.attributes.position.array;
3394
+ const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3395
+ positionTarget = new THREE2.Vector3(
3396
+ positions[midIndex],
3397
+ positions[midIndex + 1],
3398
+ positions[midIndex + 2]
3399
+ );
3400
+ } else {
3401
+ positionTarget = new THREE2.Vector3().addVectors(
3402
+ hoveredLink.userData.sourceNode.position,
3403
+ hoveredLink.userData.targetNode.position
3404
+ ).multiplyScalar(0.5);
3405
+ }
3406
+ isLink = true;
3407
+ if (tooltipEl.dataset.currentId !== currentId) {
3408
+ content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
3409
+ }
3134
3410
  }
3135
3411
  }
3136
3412
  if (positionTarget) {
@@ -3154,9 +3430,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3154
3430
  const tooltipRect = tooltipEl.getBoundingClientRect();
3155
3431
  const offsetX = 20;
3156
3432
  const offsetY = 20;
3157
- if (x + tooltipRect.width + offsetX > clientWidth) x = x - tooltipRect.width - offsetX;
3433
+ if (x + tooltipRect.width + offsetX > clientWidth)
3434
+ x = x - tooltipRect.width - offsetX;
3158
3435
  else x = x + offsetX;
3159
- if (y + tooltipRect.height + offsetY > clientHeight) y = y - tooltipRect.height - offsetY;
3436
+ if (y + tooltipRect.height + offsetY > clientHeight)
3437
+ y = y - tooltipRect.height - offsetY;
3160
3438
  else y = y + offsetY;
3161
3439
  tooltipEl.style.display = "block";
3162
3440
  tooltipEl.style.left = `${x}px`;
@@ -3190,7 +3468,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
3190
3468
  const content = parts[i + 2] || "";
3191
3469
  let finalUuid = null;
3192
3470
  if (suffix) {
3193
- const existingMatch = existingSections.find((s) => s.id && s.id.includes(suffix));
3471
+ const existingMatch = existingSections.find(
3472
+ (s) => s.id && s.id.includes(suffix)
3473
+ );
3194
3474
  if (existingMatch) {
3195
3475
  finalUuid = existingMatch.id;
3196
3476
  } else {
@@ -3281,28 +3561,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
3281
3561
  function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3282
3562
  const [width, setWidth] = useState3(initialWidth);
3283
3563
  const [isResizing, setIsResizing] = useState3(false);
3284
- const handlePointerDown = useCallback((e) => {
3285
- e.preventDefault();
3286
- e.stopPropagation();
3287
- setIsResizing(true);
3288
- const startX = e.clientX;
3289
- const startWidth = width;
3290
- const originalUserSelect = document.body.style.userSelect;
3291
- document.body.style.userSelect = "none";
3292
- const handlePointerMove = (moveEvent) => {
3293
- const deltaX = startX - moveEvent.clientX;
3294
- const newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth);
3295
- setWidth(newWidth);
3296
- };
3297
- const handlePointerUp = () => {
3298
- setIsResizing(false);
3299
- document.body.style.userSelect = originalUserSelect;
3300
- document.removeEventListener("pointermove", handlePointerMove);
3301
- document.removeEventListener("pointerup", handlePointerUp);
3302
- };
3303
- document.addEventListener("pointermove", handlePointerMove);
3304
- document.addEventListener("pointerup", handlePointerUp);
3305
- }, [width, minWidth, maxWidth]);
3564
+ const handlePointerDown = useCallback(
3565
+ (e) => {
3566
+ e.preventDefault();
3567
+ e.stopPropagation();
3568
+ setIsResizing(true);
3569
+ const startX = e.clientX;
3570
+ const startWidth = width;
3571
+ const originalUserSelect = document.body.style.userSelect;
3572
+ document.body.style.userSelect = "none";
3573
+ const handlePointerMove = (moveEvent) => {
3574
+ const deltaX = startX - moveEvent.clientX;
3575
+ const newWidth = Math.min(
3576
+ Math.max(startWidth + deltaX, minWidth),
3577
+ maxWidth
3578
+ );
3579
+ setWidth(newWidth);
3580
+ };
3581
+ const handlePointerUp = () => {
3582
+ setIsResizing(false);
3583
+ document.body.style.userSelect = originalUserSelect;
3584
+ document.removeEventListener("pointermove", handlePointerMove);
3585
+ document.removeEventListener("pointerup", handlePointerUp);
3586
+ };
3587
+ document.addEventListener("pointermove", handlePointerMove);
3588
+ document.addEventListener("pointerup", handlePointerUp);
3589
+ },
3590
+ [width, minWidth, maxWidth]
3591
+ );
3306
3592
  return { width, isResizing, handlePointerDown, setWidth };
3307
3593
  }
3308
3594
 
@@ -3439,9 +3725,9 @@ function CustomPropertyDisplay({
3439
3725
  };
3440
3726
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
3441
3727
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
3442
- const newValue = [...p.value];
3443
- newValue[j] = { ...newValue[j], [f]: v };
3444
- return { ...p, value: newValue };
3728
+ const newValue2 = [...p.value];
3729
+ newValue2[j] = { ...newValue2[j], [f]: v };
3730
+ return { ...p, value: newValue2 };
3445
3731
  });
3446
3732
  const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
3447
3733
  const renderEditView = () => {
@@ -3495,14 +3781,14 @@ function CustomPropertyDisplay({
3495
3781
  const inputClass = `${baseInput} ${noSpinnerClass}`;
3496
3782
  const handleDateTypeChange = (newDateType) => {
3497
3783
  var _a3, _b2, _c2;
3498
- let newValue = { type: newDateType };
3784
+ let newValue2 = { type: newDateType };
3499
3785
  if (newDateType === "Date Interval") {
3500
- newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3501
- newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3786
+ newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3787
+ newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3502
3788
  } else {
3503
- newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3789
+ newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3504
3790
  }
3505
- handlePropChange("value", newValue);
3791
+ handlePropChange("value", newValue2);
3506
3792
  };
3507
3793
  const handleDateValueChange = (dateField, dateValue) => {
3508
3794
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -4350,7 +4636,7 @@ ${space}${bullet} `);
4350
4636
  }
4351
4637
  ),
4352
4638
  /* @__PURE__ */ React5.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
4353
- /* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
4639
+ /* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
4354
4640
  /* @__PURE__ */ React5.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ React5.createElement(
4355
4641
  "button",
4356
4642
  {
@@ -5479,7 +5765,7 @@ function AncestryRelationshipPanel({
5479
5765
  onImageClick: handleImageClickFromText,
5480
5766
  onSaveDescription: handleSaveDescriptionInline
5481
5767
  }
5482
- ) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div", null, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React8.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ React8.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ React8.createElement("button", { onClick: onClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React8.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ React8.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React8.createElement(
5768
+ ) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React8.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ React8.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ React8.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React8.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ React8.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React8.createElement(
5483
5769
  DescriptionDisplay,
5484
5770
  {
5485
5771
  description,
@@ -5801,6 +6087,7 @@ function CreateAncestryPanel({
5801
6087
  } = ancestryMode;
5802
6088
  const [isSaving, setIsSaving] = useState11(false);
5803
6089
  const [isLinkCopied, setIsLinkCopied] = useState11(false);
6090
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState11(false);
5804
6091
  const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = useState11(false);
5805
6092
  const handleCopyLink = (e) => {
5806
6093
  e.stopPropagation();
@@ -5868,12 +6155,14 @@ function CreateAncestryPanel({
5868
6155
  };
5869
6156
  const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
5870
6157
  setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
6158
+ setHasUnsavedChanges(true);
5871
6159
  };
5872
6160
  const handleToggleAddMode = (isAbstraction = false) => {
5873
6161
  if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
5874
6162
  setTargetRenderNodeId(null);
5875
6163
  }
5876
6164
  setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
6165
+ setHasUnsavedChanges(true);
5877
6166
  };
5878
6167
  const handleRemoveNode = useCallback2((pathToRemove, isAbstraction = false) => {
5879
6168
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
@@ -5891,6 +6180,7 @@ function CreateAncestryPanel({
5891
6180
  const indexToRemove = pathToRemove[pathToRemove.length - 1];
5892
6181
  if (currentParent.children && currentParent.children.length > indexToRemove) {
5893
6182
  currentParent.children.splice(indexToRemove, 1);
6183
+ setHasUnsavedChanges(true);
5894
6184
  }
5895
6185
  return { ...prev, [treeKey]: newTree };
5896
6186
  });
@@ -5949,6 +6239,7 @@ function CreateAncestryPanel({
5949
6239
  updateGlobalTree(rootTreeClone);
5950
6240
  }
5951
6241
  setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
6242
+ setHasUnsavedChanges(true);
5952
6243
  } else {
5953
6244
  alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
5954
6245
  }
@@ -6021,6 +6312,7 @@ function CreateAncestryPanel({
6021
6312
  const handleAddProp = () => {
6022
6313
  const newProp = createNewCustomProperty(customProps);
6023
6314
  setCustomProps((p) => [...p, newProp]);
6315
+ setHasUnsavedChanges(true);
6024
6316
  setTimeout(() => {
6025
6317
  var _a;
6026
6318
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -6029,11 +6321,13 @@ function CreateAncestryPanel({
6029
6321
  const handleRemoveProp = (i) => {
6030
6322
  const newProps = customProps.filter((_, idx) => idx !== i);
6031
6323
  setCustomProps(newProps);
6324
+ setHasUnsavedChanges(true);
6032
6325
  };
6033
6326
  const handleUpdateProp = (index, updatedProp) => {
6034
6327
  const newProps = [...customProps];
6035
6328
  newProps[index] = updatedProp;
6036
6329
  setCustomProps(newProps);
6330
+ setHasUnsavedChanges(true);
6037
6331
  };
6038
6332
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
6039
6333
  useEffect10(() => {
@@ -6143,6 +6437,7 @@ function CreateAncestryPanel({
6143
6437
  updateGlobalTree(rootTreeClone);
6144
6438
  setBranchStack([...branchStack]);
6145
6439
  setIsPickerOpen(false);
6440
+ setHasUnsavedChanges(true);
6146
6441
  try {
6147
6442
  setIsSaving(true);
6148
6443
  const rootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6156,6 +6451,7 @@ function CreateAncestryPanel({
6156
6451
  rootExtras
6157
6452
  );
6158
6453
  setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
6454
+ setHasUnsavedChanges(false);
6159
6455
  if (onRenderFullAncestry) {
6160
6456
  const fullTreePayload = {
6161
6457
  ancestry_id: ancestryMode.currentAncestryId || "temp_root",
@@ -6198,6 +6494,7 @@ function CreateAncestryPanel({
6198
6494
  if (branchIndex !== -1) {
6199
6495
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6200
6496
  updateGlobalTree(rootTreeClone);
6497
+ setHasUnsavedChanges(true);
6201
6498
  try {
6202
6499
  setIsSaving(true);
6203
6500
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6219,6 +6516,7 @@ function CreateAncestryPanel({
6219
6516
  isPrivate,
6220
6517
  ancestryMode.abstraction_tree
6221
6518
  ));
6519
+ setHasUnsavedChanges(false);
6222
6520
  if (onClearAncestryVisuals) {
6223
6521
  onClearAncestryVisuals(currentStep.branchId);
6224
6522
  }
@@ -6251,6 +6549,7 @@ function CreateAncestryPanel({
6251
6549
  if (branchIndex !== -1) {
6252
6550
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6253
6551
  updateGlobalTree(rootTreeClone);
6552
+ setHasUnsavedChanges(true);
6254
6553
  try {
6255
6554
  setIsSaving(true);
6256
6555
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6272,6 +6571,7 @@ function CreateAncestryPanel({
6272
6571
  isPrivate,
6273
6572
  ancestryMode.abstraction_tree
6274
6573
  ));
6574
+ setHasUnsavedChanges(false);
6275
6575
  if (onClearAncestryVisuals) {
6276
6576
  onClearAncestryVisuals(currentStep.branchId);
6277
6577
  }
@@ -6533,6 +6833,7 @@ function CreateAncestryPanel({
6533
6833
  }
6534
6834
  setBranchStack(parentStack);
6535
6835
  setTargetScrollSectionId(targetFocusId);
6836
+ setHasUnsavedChanges(true);
6536
6837
  if (onRenderFullAncestry) {
6537
6838
  const parentStack2 = currentStack;
6538
6839
  const rotation = parentStack2.reduce((acc, step) => {
@@ -6594,7 +6895,6 @@ function CreateAncestryPanel({
6594
6895
  direction,
6595
6896
  tree: {
6596
6897
  node: nodeData,
6597
- node_id: nodeId,
6598
6898
  children: [],
6599
6899
  relationship: {}
6600
6900
  }
@@ -6616,6 +6916,7 @@ function CreateAncestryPanel({
6616
6916
  savedMaxIndex: parentIndexToSave,
6617
6917
  entryDirection: direction
6618
6918
  }]);
6919
+ setHasUnsavedChanges(true);
6619
6920
  if (branch && branch.tree && onRenderFullAncestry) {
6620
6921
  const branchAncestryObj = {
6621
6922
  ancestry_id: branch.id,
@@ -6666,6 +6967,10 @@ function CreateAncestryPanel({
6666
6967
  const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
6667
6968
  const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
6668
6969
  const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
6970
+ if (!keepOpen && !hasUnsavedChanges) {
6971
+ onClose();
6972
+ return;
6973
+ }
6669
6974
  if (!currentInputName.trim()) {
6670
6975
  alert("O nome n\xE3o pode estar vazio.");
6671
6976
  return;
@@ -6673,11 +6978,7 @@ function CreateAncestryPanel({
6673
6978
  setIsSaving(true);
6674
6979
  const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
6675
6980
  setExistingSections(processedSections);
6676
- const updatedRootTree = applyDescriptionToTree(
6677
- ancestryMode.tree,
6678
- currentInputDesc,
6679
- processedSections
6680
- );
6981
+ const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
6681
6982
  const extrasObj = {
6682
6983
  ...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
6683
6984
  is_private: isPrivate
@@ -6719,6 +7020,7 @@ function CreateAncestryPanel({
6719
7020
  isPrivate,
6720
7021
  ancestryMode.abstraction_tree
6721
7022
  ));
7023
+ setHasUnsavedChanges(false);
6722
7024
  if (onRenderFullAncestry) {
6723
7025
  const rotation = branchStack.reduce((acc, step) => {
6724
7026
  return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
@@ -6770,6 +7072,7 @@ function CreateAncestryPanel({
6770
7072
  updatedRootTree,
6771
7073
  extrasObj
6772
7074
  );
7075
+ setHasUnsavedChanges(false);
6773
7076
  setLastSavedSnapshot(takeSnapshot(
6774
7077
  updatedRootTree,
6775
7078
  currentInputName,
@@ -6792,6 +7095,7 @@ function CreateAncestryPanel({
6792
7095
  const newTreeString = JSON.stringify(newRootTree);
6793
7096
  if (currentTreeString !== newTreeString) {
6794
7097
  updateGlobalTree(newRootTree);
7098
+ setHasUnsavedChanges(true);
6795
7099
  }
6796
7100
  }, [description, existingSections]);
6797
7101
  const handleTriggerFullRender = () => {
@@ -6814,6 +7118,7 @@ function CreateAncestryPanel({
6814
7118
  };
6815
7119
  const handleSaveDescriptionInline = (newDesc) => {
6816
7120
  setDescription(newDesc);
7121
+ setHasUnsavedChanges(true);
6817
7122
  handleLocalSave(true, { description: newDesc });
6818
7123
  };
6819
7124
  const swallow = (e) => e.stopPropagation();
@@ -6943,7 +7248,11 @@ function CreateAncestryPanel({
6943
7248
  {
6944
7249
  type: "text",
6945
7250
  value: ancestryName,
6946
- onChange: (e) => setAncestryName(e.target.value),
7251
+ onChange: (e) => {
7252
+ setAncestryName(e.target.value);
7253
+ setHasUnsavedChanges(true);
7254
+ },
7255
+ readOnly: isContextLinked,
6947
7256
  placeholder: "Nome da Ancestralidade",
6948
7257
  className: "text-xl sm:text-2xl font-semibold tracking-tight bg-transparent border-none p-0 focus:ring-2 focus:ring-indigo-500 rounded-md -ml-1.5 px-1.5 w-full outline-none transition-all focus:bg-slate-800/70"
6949
7258
  }
@@ -7431,7 +7740,7 @@ function ColorPicker({ color, onChange, disabled }) {
7431
7740
  style: { backgroundColor: preset },
7432
7741
  title: preset
7433
7742
  },
7434
- color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ React12.createElement(FiCheck6, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
7743
+ (color || "").toLowerCase() === (preset || "").toLowerCase() && /* @__PURE__ */ React12.createElement(FiCheck6, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
7435
7744
  )))), /* @__PURE__ */ React12.createElement("style", null, `
7436
7745
  .custom-react-colorful .react-colorful {
7437
7746
  width: 100%;
@@ -7523,13 +7832,23 @@ function InSceneCreationForm({
7523
7832
  }, [hasImages, useImageAsTexture, onImageChange]);
7524
7833
  useEffect13(() => {
7525
7834
  let result = [];
7835
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
7526
7836
  if (typeInput.trim() === "") {
7527
- result = existingTypes.filter((t) => !types.includes(t));
7837
+ result = validExistingTypes.filter((t) => !types.includes(t));
7528
7838
  } else {
7529
- const lowercasedInput = typeInput.toLowerCase();
7530
- result = existingTypes.filter(
7531
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
7532
- );
7839
+ console.log("InSceneCreationForm: Filtrando tipos com input:", typeInput);
7840
+ try {
7841
+ const lowercasedInput = typeInput.toLowerCase();
7842
+ result = validExistingTypes.filter((t) => {
7843
+ if (!t) {
7844
+ console.warn("InSceneCreationForm: Tipo encontrado como undefined/null durante filtragem");
7845
+ return false;
7846
+ }
7847
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
7848
+ });
7849
+ } catch (err) {
7850
+ console.error("InSceneCreationForm: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
7851
+ }
7533
7852
  }
7534
7853
  if (sourceTypes) {
7535
7854
  const priorityTypes = Array.isArray(sourceTypes) ? sourceTypes : [sourceTypes];
@@ -7592,9 +7911,9 @@ function InSceneCreationForm({
7592
7911
  };
7593
7912
  const handleToggleImageMode = () => {
7594
7913
  var _a2, _b;
7595
- const newValue = !useImageAsTexture;
7596
- setUseImageAsTexture(newValue);
7597
- if (newValue) {
7914
+ const newValue2 = !useImageAsTexture;
7915
+ setUseImageAsTexture(newValue2);
7916
+ if (newValue2) {
7598
7917
  const firstImageProp = customProps.find((p) => p.type === "images");
7599
7918
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
7600
7919
  const url = firstImageProp.value[0].value;
@@ -7892,9 +8211,9 @@ function InSceneVersionForm({
7892
8211
  };
7893
8212
  const handleToggleImageMode = () => {
7894
8213
  var _a, _b;
7895
- const newValue = !useImageAsTexture;
7896
- setUseImageAsTexture(newValue);
7897
- if (newValue) {
8214
+ const newValue2 = !useImageAsTexture;
8215
+ setUseImageAsTexture(newValue2);
8216
+ if (newValue2) {
7898
8217
  const firstImageProp = customProps.find((p) => p.type === "images");
7899
8218
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
7900
8219
  const url = firstImageProp.value[0].value;
@@ -8048,7 +8367,7 @@ function InSceneVersionForm({
8048
8367
 
8049
8368
  // src/components/InSceneQuestForm.jsx
8050
8369
  import React15, { useState as useState16, useRef as useRef12 } from "react";
8051
- import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5 } from "react-icons/fi";
8370
+ import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5, FiUser, FiSearch as FiSearch4 } from "react-icons/fi";
8052
8371
  var QUEST_STATUS_COLORS2 = {
8053
8372
  "Backlog": "#64748b",
8054
8373
  "In Progress": "#eab308",
@@ -8070,10 +8389,13 @@ function InSceneQuestForm({
8070
8389
  onSizeChange,
8071
8390
  viewName = "Projeto",
8072
8391
  // NOVA PROP
8073
- questCounter = 1
8392
+ questCounter = 1,
8074
8393
  // NOVA PROP
8394
+ viewMembers = []
8075
8395
  }) {
8396
+ var _a, _b;
8076
8397
  const [name, setName] = useState16("");
8398
+ const [assigneeId, setAssigneeId] = useState16("");
8077
8399
  const [types, setTypes] = useState16(["quest"]);
8078
8400
  const [typeInput, setTypeInput] = useState16("");
8079
8401
  const [status, setStatus] = useState16("Backlog");
@@ -8081,6 +8403,8 @@ function InSceneQuestForm({
8081
8403
  const [intensity, setIntensity] = useState16(0);
8082
8404
  const [description, setDescription] = useState16("");
8083
8405
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState16(false);
8406
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState16(false);
8407
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = useState16("");
8084
8408
  const [customProps, setCustomProps] = useState16([]);
8085
8409
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
8086
8410
  const propsEndRef = useRef12(null);
@@ -8089,8 +8413,8 @@ function InSceneQuestForm({
8089
8413
  const newProp = createNewCustomProperty(customProps);
8090
8414
  setCustomProps([...customProps, newProp]);
8091
8415
  setTimeout(() => {
8092
- var _a;
8093
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
8416
+ var _a2;
8417
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
8094
8418
  }, 100);
8095
8419
  };
8096
8420
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -8136,6 +8460,7 @@ function InSceneQuestForm({
8136
8460
  type: types,
8137
8461
  color: QUEST_STATUS_COLORS2[status],
8138
8462
  status,
8463
+ assignee_id: assigneeId || null,
8139
8464
  size,
8140
8465
  intensity,
8141
8466
  description: description.trim(),
@@ -8207,7 +8532,59 @@ function InSceneQuestForm({
8207
8532
  },
8208
8533
  /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
8209
8534
  s
8210
- )))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX4, { size: 12 })))), /* @__PURE__ */ React15.createElement(
8535
+ )))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
8536
+ "button",
8537
+ {
8538
+ type: "button",
8539
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
8540
+ 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"
8541
+ },
8542
+ /* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React15.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")),
8543
+ /* @__PURE__ */ React15.createElement(FiChevronDown5, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
8544
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
8545
+ setIsAssigneeDropdownOpen(false);
8546
+ setAssigneeSearchQuery("");
8547
+ } }), /* @__PURE__ */ React15.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__ */ React15.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(FiSearch4, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ React15.createElement(
8548
+ "input",
8549
+ {
8550
+ type: "text",
8551
+ autoFocus: true,
8552
+ placeholder: "Buscar membro...",
8553
+ value: assigneeSearchQuery,
8554
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
8555
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
8556
+ onClick: (e) => e.stopPropagation()
8557
+ }
8558
+ )), /* @__PURE__ */ React15.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React15.createElement(
8559
+ "li",
8560
+ {
8561
+ onClick: () => {
8562
+ setAssigneeId("");
8563
+ setIsAssigneeDropdownOpen(false);
8564
+ setAssigneeSearchQuery("");
8565
+ },
8566
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!assigneeId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
8567
+ },
8568
+ "Nenhum"
8569
+ ), viewMembers.filter((member) => {
8570
+ const search = assigneeSearchQuery.toLowerCase();
8571
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8572
+ }).map((member) => /* @__PURE__ */ React15.createElement(
8573
+ "li",
8574
+ {
8575
+ key: member.id,
8576
+ onClick: () => {
8577
+ setAssigneeId(member.id);
8578
+ setIsAssigneeDropdownOpen(false);
8579
+ setAssigneeSearchQuery("");
8580
+ },
8581
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${assigneeId === member.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
8582
+ },
8583
+ member.name || member.email || member.id
8584
+ )), viewMembers.filter((member) => {
8585
+ const search = assigneeSearchQuery.toLowerCase();
8586
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8587
+ }).length === 0 && assigneeSearchQuery && /* @__PURE__ */ React15.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado")))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX4, { size: 12 })))), /* @__PURE__ */ React15.createElement(
8211
8588
  "input",
8212
8589
  {
8213
8590
  type: "text",
@@ -8314,6 +8691,7 @@ function NodeDetailsPanel({
8314
8691
  return !!(node == null ? void 0 : node.useImageAsTexture);
8315
8692
  });
8316
8693
  const [selectedImageUrl, setSelectedImageUrl] = useState17((node == null ? void 0 : node.textureImageUrl) ?? null);
8694
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState17(false);
8317
8695
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8318
8696
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8319
8697
  initialWidth: isReadMode ? 700 : 440,
@@ -8351,6 +8729,7 @@ function NodeDetailsPanel({
8351
8729
  else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
8352
8730
  else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
8353
8731
  setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
8732
+ setHasUnsavedChanges(false);
8354
8733
  }
8355
8734
  }, [node]);
8356
8735
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
@@ -8361,15 +8740,22 @@ function NodeDetailsPanel({
8361
8740
  }
8362
8741
  }, [hasImages, useImageAsTexture]);
8363
8742
  useEffect15(() => {
8743
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8364
8744
  if (typeInput.trim() === "") {
8365
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
8745
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8366
8746
  } else {
8367
- const lowercasedInput = typeInput.toLowerCase();
8368
- setFilteredTypes(
8369
- existingTypes.filter(
8370
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8371
- )
8372
- );
8747
+ console.log("NodeDetailsPanel: Filtrando tipos com input:", typeInput);
8748
+ try {
8749
+ const lowercasedInput = typeInput.toLowerCase();
8750
+ setFilteredTypes(
8751
+ validExistingTypes.filter((t) => {
8752
+ if (!t) return false;
8753
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
8754
+ })
8755
+ );
8756
+ } catch (err) {
8757
+ console.error("NodeDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
8758
+ }
8373
8759
  }
8374
8760
  }, [typeInput, existingTypes, types]);
8375
8761
  const handleIntensityChangeLocal = (e) => {
@@ -8377,6 +8763,7 @@ function NodeDetailsPanel({
8377
8763
  setIntensity(val);
8378
8764
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
8379
8765
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
8766
+ setHasUnsavedChanges(true);
8380
8767
  };
8381
8768
  const handleCopyLink = () => {
8382
8769
  if (!(node == null ? void 0 : node.id)) return;
@@ -8394,14 +8781,17 @@ function NodeDetailsPanel({
8394
8781
  const v = e.target.value;
8395
8782
  setName(v);
8396
8783
  onNameChange == null ? void 0 : onNameChange(node.id, v);
8784
+ setHasUnsavedChanges(true);
8397
8785
  };
8398
8786
  const handleColorChange = (val) => {
8399
8787
  setColor(val);
8400
8788
  onColorChange == null ? void 0 : onColorChange(node.id, val);
8789
+ setHasUnsavedChanges(true);
8401
8790
  };
8402
8791
  const handleSizeChange = (newSize) => {
8403
8792
  setSize(newSize);
8404
8793
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
8794
+ setHasUnsavedChanges(true);
8405
8795
  };
8406
8796
  const handleAddType = (newType) => {
8407
8797
  const trimmed = newType.trim();
@@ -8409,10 +8799,12 @@ function NodeDetailsPanel({
8409
8799
  setTypes([...types, trimmed]);
8410
8800
  setTypeInput("");
8411
8801
  setShowTypeSuggestions(false);
8802
+ setHasUnsavedChanges(true);
8412
8803
  }
8413
8804
  };
8414
8805
  const handleRemoveType = (indexToRemove) => {
8415
8806
  setTypes(types.filter((_, index) => index !== indexToRemove));
8807
+ setHasUnsavedChanges(true);
8416
8808
  };
8417
8809
  const handleTypeInputKeyDown = (e) => {
8418
8810
  if (e.key === "Enter") {
@@ -8425,6 +8817,7 @@ function NodeDetailsPanel({
8425
8817
  const handleAddProp = () => {
8426
8818
  const newProp = createNewCustomProperty(customProps);
8427
8819
  setCustomProps((p) => [...p, newProp]);
8820
+ setHasUnsavedChanges(true);
8428
8821
  setTimeout(() => {
8429
8822
  var _a;
8430
8823
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8433,19 +8826,21 @@ function NodeDetailsPanel({
8433
8826
  const handleRemoveProp = (i) => {
8434
8827
  const newProps = customProps.filter((_, idx) => idx !== i);
8435
8828
  setCustomProps(newProps);
8829
+ setHasUnsavedChanges(true);
8436
8830
  triggerAutoSave({ customProps: newProps });
8437
8831
  };
8438
8832
  const handleUpdateProp = (index, updatedProp) => {
8439
8833
  const newProps = [...customProps];
8440
8834
  newProps[index] = updatedProp;
8441
8835
  setCustomProps(newProps);
8836
+ setHasUnsavedChanges(true);
8442
8837
  if (!updatedProp.isEditing) {
8443
8838
  triggerAutoSave({ customProps: newProps });
8444
8839
  }
8445
8840
  };
8446
8841
  const handleToggleImageMode = () => {
8447
- const newValue = !useImageAsTexture;
8448
8842
  setUseImageAsTexture(newValue);
8843
+ setHasUnsavedChanges(true);
8449
8844
  let activeUrl = null;
8450
8845
  if (newValue) {
8451
8846
  const firstImageProp = customProps.find((p) => p.type === "images");
@@ -8467,6 +8862,7 @@ function NodeDetailsPanel({
8467
8862
  };
8468
8863
  const handleSelectTexture = (url) => {
8469
8864
  setSelectedImageUrl(url);
8865
+ setHasUnsavedChanges(true);
8470
8866
  onImageChange == null ? void 0 : onImageChange(true, url, color);
8471
8867
  onDataUpdate == null ? void 0 : onDataUpdate({
8472
8868
  ...node,
@@ -8477,6 +8873,7 @@ function NodeDetailsPanel({
8477
8873
  };
8478
8874
  const handleSaveDescriptionInline = (newDescription) => {
8479
8875
  setDescription(newDescription);
8876
+ setHasUnsavedChanges(true);
8480
8877
  onDataUpdate({ ...node, description: newDescription });
8481
8878
  triggerAutoSave({ description: newDescription });
8482
8879
  };
@@ -8487,6 +8884,10 @@ function NodeDetailsPanel({
8487
8884
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8488
8885
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8489
8886
  const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
8887
+ if (!keepOpen && !hasUnsavedChanges) {
8888
+ onClose();
8889
+ return;
8890
+ }
8490
8891
  if (!currentName.trim() || currentTypes.length === 0) {
8491
8892
  alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8492
8893
  return;
@@ -8511,6 +8912,7 @@ function NodeDetailsPanel({
8511
8912
  };
8512
8913
  await onSave(dataToSave, keepOpen);
8513
8914
  onDataUpdate(dataToSave);
8915
+ setHasUnsavedChanges(false);
8514
8916
  if (!keepOpen) {
8515
8917
  onClose();
8516
8918
  }
@@ -8577,7 +8979,7 @@ function NodeDetailsPanel({
8577
8979
  onImageClick: handleImageClickFromText,
8578
8980
  onSaveDescription: handleSaveDescriptionInline
8579
8981
  }
8580
- ) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div", null, /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React16.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React16.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React16.createElement(
8982
+ ) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React16.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React16.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React16.createElement(
8581
8983
  "button",
8582
8984
  {
8583
8985
  onClick: handleCopyLink,
@@ -8585,7 +8987,7 @@ function NodeDetailsPanel({
8585
8987
  title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
8586
8988
  },
8587
8989
  isLinkCopied ? /* @__PURE__ */ React16.createElement(FiCheck10, { size: 12 }) : /* @__PURE__ */ React16.createElement(FiLink5, { size: 12 })
8588
- )), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
8990
+ )), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
8589
8991
  "button",
8590
8992
  {
8591
8993
  type: "button",
@@ -8747,6 +9149,7 @@ function NodeDetailsPanel({
8747
9149
  initialValue: description,
8748
9150
  onSave: (newDescription) => {
8749
9151
  setDescription(newDescription);
9152
+ setHasUnsavedChanges(true);
8750
9153
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
8751
9154
  triggerAutoSave({ description: newDescription });
8752
9155
  },
@@ -8760,7 +9163,7 @@ function NodeDetailsPanel({
8760
9163
 
8761
9164
  // src/components/QuestDetailsPanel.jsx
8762
9165
  import React17, { useState as useState18, useEffect as useEffect16, useRef as useRef14 } from "react";
8763
- import { FiPlus as FiPlus7, FiX as FiX6, FiCheck as FiCheck11, FiEdit2 as FiEdit28, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4, FiLink as FiLink6, FiTarget as FiTarget2, FiChevronDown as FiChevronDown6 } from "react-icons/fi";
9166
+ import { FiPlus as FiPlus7, FiX as FiX6, FiCheck as FiCheck11, FiEdit2 as FiEdit28, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4, FiLink as FiLink6, FiTarget as FiTarget2, FiChevronDown as FiChevronDown6, FiUser as FiUser2, FiSearch as FiSearch5 } from "react-icons/fi";
8764
9167
  var QUEST_STATUS_COLORS3 = {
8765
9168
  "Backlog": "#64748b",
8766
9169
  "In Progress": "#eab308",
@@ -8783,7 +9186,8 @@ function QuestDetailsPanel({
8783
9186
  onMentionClick,
8784
9187
  onUploadFile,
8785
9188
  userRole,
8786
- currentDatasetName
9189
+ currentDatasetName,
9190
+ viewMembers = []
8787
9191
  }) {
8788
9192
  var _a;
8789
9193
  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) || "";
@@ -8795,9 +9199,12 @@ function QuestDetailsPanel({
8795
9199
  const [typeInput, setTypeInput] = useState18("");
8796
9200
  const [status, setStatus] = useState18((node == null ? void 0 : node.status) ?? "Backlog");
8797
9201
  const [size, setSize] = useState18((node == null ? void 0 : node.size) ?? "medium");
9202
+ const [assigneeId, setAssigneeId] = useState18((node == null ? void 0 : node.assignee_id) || "");
8798
9203
  const [description, setDescription] = useState18((node == null ? void 0 : node.description) ?? "");
8799
9204
  const [intensity, setIntensity] = useState18((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8800
9205
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState18(false);
9206
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState18(false);
9207
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = useState18("");
8801
9208
  const [customProps, setCustomProps] = useState18(() => extractCustomPropsFromNode(node || {}));
8802
9209
  const [showTypeSuggestions, setShowTypeSuggestions] = useState18(false);
8803
9210
  const [filteredTypes, setFilteredTypes] = useState18([]);
@@ -8806,6 +9213,7 @@ function QuestDetailsPanel({
8806
9213
  const [existingSections, setExistingSections] = useState18((node == null ? void 0 : node.description_sections) || []);
8807
9214
  const [isSaving, setIsSaving] = useState18(false);
8808
9215
  const [isLinkCopied, setIsLinkCopied] = useState18(false);
9216
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState18(false);
8809
9217
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8810
9218
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8811
9219
  initialWidth: isReadMode ? 700 : 440,
@@ -8833,22 +9241,34 @@ function QuestDetailsPanel({
8833
9241
  setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8834
9242
  setStatus((node == null ? void 0 : node.status) ?? "Backlog");
8835
9243
  setSize((node == null ? void 0 : node.size) ?? "medium");
9244
+ setAssigneeId((node == null ? void 0 : node.assignee_id) || "");
8836
9245
  setDescription((node == null ? void 0 : node.description) ?? "");
8837
9246
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8838
9247
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
8839
9248
  setCustomProps(extractCustomPropsFromNode(node || {}));
9249
+ setHasUnsavedChanges(false);
8840
9250
  }
8841
9251
  }, [node]);
8842
9252
  useEffect16(() => {
9253
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8843
9254
  if (typeInput.trim() === "") {
8844
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
9255
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8845
9256
  } else {
8846
- const lowercasedInput = typeInput.toLowerCase();
8847
- setFilteredTypes(
8848
- existingTypes.filter(
8849
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8850
- )
8851
- );
9257
+ console.log("QuestDetailsPanel: Filtrando tipos com input:", typeInput);
9258
+ try {
9259
+ const lowercasedInput = typeInput.toLowerCase();
9260
+ setFilteredTypes(
9261
+ validExistingTypes.filter((t) => {
9262
+ if (!t) {
9263
+ console.warn("QuestDetailsPanel: Tipo encontrado como undefined/null durante filtragem");
9264
+ return false;
9265
+ }
9266
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
9267
+ })
9268
+ );
9269
+ } catch (err) {
9270
+ console.error("QuestDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
9271
+ }
8852
9272
  }
8853
9273
  }, [typeInput, existingTypes, types]);
8854
9274
  const handleCopyLink = () => {
@@ -8868,16 +9288,19 @@ function QuestDetailsPanel({
8868
9288
  setRawTitle(val);
8869
9289
  const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8870
9290
  onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
9291
+ setHasUnsavedChanges(true);
8871
9292
  };
8872
9293
  const handleSizeChange = (newSize) => {
8873
9294
  setSize(newSize);
8874
9295
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
9296
+ setHasUnsavedChanges(true);
8875
9297
  };
8876
9298
  const handleStatusChange = (newStatus) => {
8877
9299
  setStatus(newStatus);
8878
9300
  const newColor = QUEST_STATUS_COLORS3[newStatus];
8879
9301
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
8880
9302
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
9303
+ setHasUnsavedChanges(true);
8881
9304
  };
8882
9305
  const handleAddType = (newType) => {
8883
9306
  const trimmed = newType.trim();
@@ -8885,11 +9308,13 @@ function QuestDetailsPanel({
8885
9308
  setTypes([...types, trimmed]);
8886
9309
  setTypeInput("");
8887
9310
  setShowTypeSuggestions(false);
9311
+ setHasUnsavedChanges(true);
8888
9312
  }
8889
9313
  };
8890
9314
  const handleRemoveType = (indexToRemove) => {
8891
9315
  if (types[indexToRemove] === "quest") return;
8892
9316
  setTypes(types.filter((_, index) => index !== indexToRemove));
9317
+ setHasUnsavedChanges(true);
8893
9318
  };
8894
9319
  const handleTypeInputKeyDown = (e) => {
8895
9320
  if (e.key === "Enter") {
@@ -8902,6 +9327,7 @@ function QuestDetailsPanel({
8902
9327
  const handleAddProp = () => {
8903
9328
  const newProp = createNewCustomProperty(customProps);
8904
9329
  setCustomProps((p) => [...p, newProp]);
9330
+ setHasUnsavedChanges(true);
8905
9331
  setTimeout(() => {
8906
9332
  var _a2;
8907
9333
  (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8910,18 +9336,21 @@ function QuestDetailsPanel({
8910
9336
  const handleRemoveProp = (i) => {
8911
9337
  const newProps = customProps.filter((_, idx) => idx !== i);
8912
9338
  setCustomProps(newProps);
9339
+ setHasUnsavedChanges(true);
8913
9340
  triggerAutoSave({ customProps: newProps });
8914
9341
  };
8915
9342
  const handleUpdateProp = (index, updatedProp) => {
8916
9343
  const newProps = [...customProps];
8917
9344
  newProps[index] = updatedProp;
8918
9345
  setCustomProps(newProps);
9346
+ setHasUnsavedChanges(true);
8919
9347
  if (!updatedProp.isEditing) {
8920
9348
  triggerAutoSave({ customProps: newProps });
8921
9349
  }
8922
9350
  };
8923
9351
  const handleSaveDescriptionInline = (newDescription) => {
8924
9352
  setDescription(newDescription);
9353
+ setHasUnsavedChanges(true);
8925
9354
  onDataUpdate({ ...node, description: newDescription });
8926
9355
  triggerAutoSave({ description: newDescription });
8927
9356
  };
@@ -8929,10 +9358,15 @@ function QuestDetailsPanel({
8929
9358
  const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
8930
9359
  const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
8931
9360
  const currentTypes = overrides.types !== void 0 ? overrides.types : types;
9361
+ const currentAssigneeId = overrides.assigneeId !== void 0 ? overrides.assigneeId : assigneeId;
8932
9362
  const currentDescription = overrides.description !== void 0 ? overrides.description : description;
8933
9363
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8934
9364
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8935
9365
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
9366
+ if (!keepOpen && !hasUnsavedChanges) {
9367
+ onClose();
9368
+ return;
9369
+ }
8936
9370
  if (!currentRawTitle.trim() || currentTypes.length === 0) {
8937
9371
  alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8938
9372
  return;
@@ -8950,6 +9384,7 @@ function QuestDetailsPanel({
8950
9384
  type: currentTypes,
8951
9385
  color: QUEST_STATUS_COLORS3[currentStatus],
8952
9386
  status: currentStatus,
9387
+ assignee_id: currentAssigneeId || null,
8953
9388
  size,
8954
9389
  description: currentDescription,
8955
9390
  description_sections: processedSections,
@@ -8962,6 +9397,7 @@ function QuestDetailsPanel({
8962
9397
  };
8963
9398
  await onSave(dataToSave, keepOpen);
8964
9399
  onDataUpdate(dataToSave);
9400
+ setHasUnsavedChanges(false);
8965
9401
  if (!keepOpen) {
8966
9402
  onClose();
8967
9403
  }
@@ -8981,6 +9417,8 @@ function QuestDetailsPanel({
8981
9417
  onClose();
8982
9418
  };
8983
9419
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
9420
+ const assigneeMember = viewMembers.find((m) => m.id === assigneeId);
9421
+ const isAssigneeUndefined = assigneeId && !assigneeMember;
8984
9422
  return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(
8985
9423
  "div",
8986
9424
  {
@@ -9019,7 +9457,7 @@ function QuestDetailsPanel({
9019
9457
  onImageClick: handleImageClickFromText,
9020
9458
  onSaveDescription: handleSaveDescriptionInline
9021
9459
  }
9022
- ) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", null, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
9460
+ ) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
9023
9461
  "input",
9024
9462
  {
9025
9463
  type: "text",
@@ -9050,7 +9488,71 @@ function QuestDetailsPanel({
9050
9488
  },
9051
9489
  /* @__PURE__ */ React17.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS3[s] } }),
9052
9490
  s
9053
- )))))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React17.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React17.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, canEdit && t !== "quest" && /* @__PURE__ */ React17.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React17.createElement(FiX6, { size: 12 })))), canEdit && /* @__PURE__ */ React17.createElement(
9491
+ )))))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), canEdit ? /* @__PURE__ */ React17.createElement("div", { className: "relative" }, /* @__PURE__ */ React17.createElement(
9492
+ "button",
9493
+ {
9494
+ type: "button",
9495
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9496
+ 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"
9497
+ },
9498
+ /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(FiUser2, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React17.createElement("span", { className: "text-slate-200 font-medium" }, isAssigneeUndefined ? "Undefined" : (assigneeMember == null ? void 0 : assigneeMember.name) || (assigneeMember == null ? void 0 : assigneeMember.email) || "Nenhum")),
9499
+ /* @__PURE__ */ React17.createElement(FiChevronDown6, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
9500
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
9501
+ setIsAssigneeDropdownOpen(false);
9502
+ setAssigneeSearchQuery("");
9503
+ } }), /* @__PURE__ */ React17.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__ */ React17.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(FiSearch5, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ React17.createElement(
9504
+ "input",
9505
+ {
9506
+ type: "text",
9507
+ autoFocus: true,
9508
+ placeholder: "Buscar membro...",
9509
+ value: assigneeSearchQuery,
9510
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
9511
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9512
+ onClick: (e) => e.stopPropagation()
9513
+ }
9514
+ )), /* @__PURE__ */ React17.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React17.createElement(
9515
+ "li",
9516
+ {
9517
+ onClick: () => {
9518
+ setAssigneeId("");
9519
+ setHasUnsavedChanges(true);
9520
+ setIsAssigneeDropdownOpen(false);
9521
+ setAssigneeSearchQuery("");
9522
+ },
9523
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!assigneeId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
9524
+ },
9525
+ "Nenhum"
9526
+ ), isAssigneeUndefined && /* @__PURE__ */ React17.createElement(
9527
+ "li",
9528
+ {
9529
+ onClick: () => {
9530
+ setIsAssigneeDropdownOpen(false);
9531
+ setAssigneeSearchQuery("");
9532
+ },
9533
+ className: "px-3 py-2.5 text-sm cursor-pointer bg-red-500/10 text-red-300 flex items-center gap-2"
9534
+ },
9535
+ "Undefined (Removido)"
9536
+ ), viewMembers.filter((member) => {
9537
+ const search = assigneeSearchQuery.toLowerCase();
9538
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9539
+ }).map((member) => /* @__PURE__ */ React17.createElement(
9540
+ "li",
9541
+ {
9542
+ key: member.id,
9543
+ onClick: () => {
9544
+ setAssigneeId(member.id);
9545
+ setHasUnsavedChanges(true);
9546
+ setIsAssigneeDropdownOpen(false);
9547
+ setAssigneeSearchQuery("");
9548
+ },
9549
+ className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${assigneeId === member.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
9550
+ },
9551
+ member.name || member.email || member.id
9552
+ )), viewMembers.filter((member) => {
9553
+ const search = assigneeSearchQuery.toLowerCase();
9554
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9555
+ }).length === 0 && assigneeSearchQuery && /* @__PURE__ */ React17.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado"))))) : /* @__PURE__ */ React17.createElement("div", { className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 text-slate-400 flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(FiUser2, { className: "opacity-50", size: 14 }), assigneeId ? assigneeMember ? assigneeMember.name || assigneeMember.email : "Undefined" : "Nenhum")), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React17.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React17.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, canEdit && t !== "quest" && /* @__PURE__ */ React17.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React17.createElement(FiX6, { size: 12 })))), canEdit && /* @__PURE__ */ React17.createElement(
9054
9556
  "input",
9055
9557
  {
9056
9558
  type: "text",
@@ -9193,9 +9695,12 @@ function RelationshipDetailsPanel({
9193
9695
  const [description, setDescription] = useState20((link == null ? void 0 : link.description) ?? "");
9194
9696
  const [customProps, setCustomProps] = useState20(() => extractCustomPropsFromNode(link || {}));
9195
9697
  const [existingSections, setExistingSections] = useState20((link == null ? void 0 : link.description_sections) || []);
9698
+ const [sourceLabel, setSourceLabel] = useState20((link == null ? void 0 : link.source_label) ?? "");
9699
+ const [targetLabel, setTargetLabel] = useState20((link == null ? void 0 : link.target_label) ?? "");
9196
9700
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState20(false);
9197
9701
  const [isSaving, setIsSaving] = useState20(false);
9198
9702
  const [isReadMode, setIsReadMode] = useState20(false);
9703
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState20(false);
9199
9704
  const propsEndRef = useRef16(null);
9200
9705
  const canEdit = useMemo9(() => {
9201
9706
  const ability = defineAbilityFor(userRole);
@@ -9206,12 +9711,16 @@ function RelationshipDetailsPanel({
9206
9711
  setDescription((link == null ? void 0 : link.description) ?? "");
9207
9712
  setExistingSections((link == null ? void 0 : link.description_sections) || []);
9208
9713
  setCustomProps(extractCustomPropsFromNode(link || {}));
9714
+ setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
9715
+ setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
9716
+ setHasUnsavedChanges(false);
9209
9717
  }, [link]);
9210
9718
  const swallow = (e) => e.stopPropagation();
9211
9719
  const handleAddProp = () => {
9212
9720
  if (!canEdit) return;
9213
9721
  const newProp = createNewCustomProperty(customProps);
9214
9722
  setCustomProps((p) => [...p, newProp]);
9723
+ setHasUnsavedChanges(true);
9215
9724
  setTimeout(() => {
9216
9725
  var _a;
9217
9726
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -9223,6 +9732,12 @@ function RelationshipDetailsPanel({
9223
9732
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
9224
9733
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
9225
9734
  const currentName = overrides.name !== void 0 ? overrides.name : name;
9735
+ const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
9736
+ const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
9737
+ if (!keepOpen && !hasUnsavedChanges) {
9738
+ onClose();
9739
+ return;
9740
+ }
9226
9741
  setIsSaving(true);
9227
9742
  try {
9228
9743
  const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
@@ -9238,8 +9753,11 @@ function RelationshipDetailsPanel({
9238
9753
  isCurved: link.isCurved,
9239
9754
  curveOffset: link.curveOffset
9240
9755
  };
9756
+ if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
9757
+ if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
9241
9758
  await onSave(dataToSave, keepOpen);
9242
9759
  onDataUpdate(dataToSave);
9760
+ setHasUnsavedChanges(false);
9243
9761
  if (!keepOpen) {
9244
9762
  onClose();
9245
9763
  }
@@ -9253,18 +9771,21 @@ function RelationshipDetailsPanel({
9253
9771
  const handleSaveDescriptionInline = (newDescription) => {
9254
9772
  if (!canEdit) return;
9255
9773
  setDescription(newDescription);
9774
+ setHasUnsavedChanges(true);
9256
9775
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9257
9776
  triggerAutoSave({ description: newDescription });
9258
9777
  };
9259
9778
  const handleRemoveProp = (i) => {
9260
9779
  const newProps = customProps.filter((_, idx) => idx !== i);
9261
9780
  setCustomProps(newProps);
9781
+ setHasUnsavedChanges(true);
9262
9782
  triggerAutoSave({ customProps: newProps });
9263
9783
  };
9264
9784
  const handleUpdateProp = (index, updatedProp) => {
9265
9785
  const newProps = [...customProps];
9266
9786
  newProps[index] = updatedProp;
9267
9787
  setCustomProps(newProps);
9788
+ setHasUnsavedChanges(true);
9268
9789
  if (!updatedProp.isEditing) {
9269
9790
  triggerAutoSave({ customProps: newProps });
9270
9791
  }
@@ -9312,19 +9833,52 @@ function RelationshipDetailsPanel({
9312
9833
  onImageClick: handleImageClickFromText,
9313
9834
  onSaveDescription: handleSaveDescriptionInline
9314
9835
  }
9315
- ) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div", null, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React19.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ React19.createElement(
9836
+ ) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React19.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ React19.createElement(
9316
9837
  "input",
9317
9838
  {
9318
9839
  type: "text",
9319
9840
  value: name,
9320
- onChange: (e) => setName(e.target.value),
9841
+ onChange: (e) => {
9842
+ setName(e.target.value);
9843
+ setHasUnsavedChanges(true);
9844
+ },
9321
9845
  placeholder: "Ex: Controla, Pertence a, Fornece...",
9322
9846
  disabled: !canEdit,
9323
9847
  className: `w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60
9324
9848
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9325
9849
  `
9326
9850
  }
9327
- )), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
9851
+ )), /* @__PURE__ */ React19.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React19.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React19.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React19.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ React19.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ React19.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ React19.createElement(
9852
+ "input",
9853
+ {
9854
+ type: "text",
9855
+ value: sourceLabel,
9856
+ onChange: (e) => {
9857
+ setSourceLabel(e.target.value);
9858
+ setHasUnsavedChanges(true);
9859
+ },
9860
+ placeholder: "Ex: Conceitos",
9861
+ disabled: !canEdit,
9862
+ className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
9863
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9864
+ `
9865
+ }
9866
+ )), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ React19.createElement(
9867
+ "input",
9868
+ {
9869
+ type: "text",
9870
+ value: targetLabel,
9871
+ onChange: (e) => {
9872
+ setTargetLabel(e.target.value);
9873
+ setHasUnsavedChanges(true);
9874
+ },
9875
+ placeholder: "Ex: Refer\xEAncias",
9876
+ disabled: !canEdit,
9877
+ className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
9878
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9879
+ `
9880
+ }
9881
+ )))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
9328
9882
  DescriptionDisplay,
9329
9883
  {
9330
9884
  description,
@@ -9394,6 +9948,7 @@ function RelationshipDetailsPanel({
9394
9948
  onSave: (newDescription) => {
9395
9949
  if (!canEdit) return;
9396
9950
  setDescription(newDescription);
9951
+ setHasUnsavedChanges(true);
9397
9952
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9398
9953
  triggerAutoSave({ description: newDescription });
9399
9954
  },
@@ -9841,7 +10396,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9841
10396
  onMentionClick,
9842
10397
  onImageClick: handleImageClickFromText
9843
10398
  }
9844
- ) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div", null, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React23.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React23.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React23.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React23.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React23.createElement("button", { onClick: onClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React23.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React23.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React23.createElement(
10399
+ ) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React23.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React23.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React23.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React23.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React23.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React23.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React23.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React23.createElement(
9845
10400
  "button",
9846
10401
  {
9847
10402
  onClick: () => setIsReadMode(true),
@@ -9875,7 +10430,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9875
10430
  // src/components/AncestryBoard.jsx
9876
10431
  import React24, { useState as useState24, useMemo as useMemo11, useEffect as useEffect21, useRef as useRef18 } from "react";
9877
10432
  import {
9878
- FiSearch as FiSearch4,
10433
+ FiSearch as FiSearch6,
9879
10434
  FiLayers as FiLayers6,
9880
10435
  FiCornerUpRight as FiCornerUpRight4,
9881
10436
  FiPlay,
@@ -10000,7 +10555,7 @@ var GroupItem = ({
10000
10555
  `,
10001
10556
  title: "Adicionar Ancestralidade a este grupo"
10002
10557
  },
10003
- isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch4, { size: 12 }),
10558
+ isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch6, { size: 12 }),
10004
10559
  isPickingForThisGroup ? "Selecionando..." : "Adicionar"
10005
10560
  ), /* @__PURE__ */ React24.createElement(
10006
10561
  "button",
@@ -10335,7 +10890,7 @@ function AncestryBoard({
10335
10890
  `
10336
10891
  },
10337
10892
  /* @__PURE__ */ React24.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ React24.createElement("div", { className: "relative group" }, /* @__PURE__ */ React24.createElement(
10338
- FiSearch4,
10893
+ FiSearch6,
10339
10894
  {
10340
10895
  className: `absolute left-3 top-1/2 -translate-y-1/2 transition-colors ${pickingGroupId ? "text-indigo-400" : "text-slate-500 group-focus-within:text-indigo-400"}`
10341
10896
  }
@@ -10493,7 +11048,8 @@ function XViewScene({
10493
11048
  save_ancestry_board_action,
10494
11049
  upload_file_action,
10495
11050
  delete_file_action,
10496
- check_user_permission
11051
+ check_user_permission,
11052
+ get_view_members
10497
11053
  }) {
10498
11054
  var _a, _b, _c, _d, _e, _f, _g, _h;
10499
11055
  const { data: session, status } = useSession();
@@ -10526,6 +11082,13 @@ function XViewScene({
10526
11082
  } else {
10527
11083
  setPermissionStatus("denied");
10528
11084
  setIsLoading(false);
11085
+ return;
11086
+ }
11087
+ if (get_view_members) {
11088
+ const membersRes = await get_view_members(owner_id, type, id);
11089
+ if (membersRes.success && membersRes.members) {
11090
+ setViewMembers(membersRes.members);
11091
+ }
10529
11092
  }
10530
11093
  } catch (error) {
10531
11094
  console.error("Erro ao verificar permiss\xE3o:", error);
@@ -10566,6 +11129,7 @@ function XViewScene({
10566
11129
  const [isLoading, setIsLoading] = useState25(true);
10567
11130
  const [permissionStatus, setPermissionStatus] = useState25("loading");
10568
11131
  const [userPermissionRole, setUserPermissionRole] = useState25(null);
11132
+ const [viewMembers, setViewMembers] = useState25([]);
10569
11133
  const [isInitialized, setIsInitialized] = useState25(false);
10570
11134
  const [sceneVersion, setSceneVersion] = useState25(0);
10571
11135
  const [contextMenu, setContextMenu] = useState25({
@@ -10634,6 +11198,7 @@ function XViewScene({
10634
11198
  });
10635
11199
  const [isImportModalOpen, setIsImportModalOpen] = useState25(false);
10636
11200
  const [importSuccessMessage, setImportSuccessMessage] = useState25("");
11201
+ const [invalidTargetError, setInvalidTargetError] = useState25(null);
10637
11202
  const [highlightedNodeId, setHighlightedNodeId] = useState25(null);
10638
11203
  const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState25(false);
10639
11204
  const [ancestryBoardData, setAncestryBoardData] = useState25([]);
@@ -12482,6 +13047,28 @@ function XViewScene({
12482
13047
  const handleStartVersioning = (nodeData) => {
12483
13048
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
12484
13049
  };
13050
+ const handleStartQuestQuick = useCallback4((questNode) => {
13051
+ var _a2;
13052
+ if (!questNode || !actionHandlerContext) return;
13053
+ const {
13054
+ labelObject,
13055
+ labelOffset,
13056
+ aura,
13057
+ borderRing,
13058
+ timelineIntervalBar,
13059
+ timelineEndLabel,
13060
+ ...cleanQuestNode
13061
+ } = questNode;
13062
+ const updatedNode = {
13063
+ ...cleanQuestNode,
13064
+ status: "In Progress",
13065
+ color: "#eab308",
13066
+ assignee_id: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id
13067
+ };
13068
+ if (userActionHandlers.handleSaveNodeDetails) {
13069
+ userActionHandlers.handleSaveNodeDetails(actionHandlerContext, updatedNode);
13070
+ }
13071
+ }, [session, actionHandlerContext]);
12485
13072
  const handleCancelQuest = useCallback4(() => {
12486
13073
  const { graphGroup, ghostElements } = stateRef.current;
12487
13074
  if (ghostElements.node && graphGroup) {
@@ -14166,7 +14753,13 @@ function XViewScene({
14166
14753
  if (!parentDataRef.current) {
14167
14754
  return [];
14168
14755
  }
14169
- return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes).filter((node) => {
14756
+ return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
14757
+ const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
14758
+ return (fileData.nodes || []).map((node) => ({
14759
+ ...node,
14760
+ dataset_name: datasetName
14761
+ }));
14762
+ }).filter((node) => {
14170
14763
  var _a2;
14171
14764
  return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
14172
14765
  });
@@ -14310,6 +14903,9 @@ function XViewScene({
14310
14903
  }, 300);
14311
14904
  } else {
14312
14905
  setHasFocusedInitial(true);
14906
+ setInvalidTargetError(
14907
+ "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
14908
+ );
14313
14909
  }
14314
14910
  }
14315
14911
  }, [
@@ -14332,6 +14928,9 @@ function XViewScene({
14332
14928
  }, 300);
14333
14929
  } else {
14334
14930
  setHasOpenedInitialAncestry(true);
14931
+ setInvalidTargetError(
14932
+ "O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
14933
+ );
14335
14934
  }
14336
14935
  }
14337
14936
  }, [
@@ -14625,7 +15224,8 @@ function XViewScene({
14625
15224
  availableNodes: allAvailableNodes,
14626
15225
  availableAncestries: allAvailableAncestries,
14627
15226
  viewName: viewParams == null ? void 0 : viewParams.name,
14628
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1
15227
+ questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
15228
+ viewMembers
14629
15229
  }
14630
15230
  ),
14631
15231
  readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ React25.createElement(
@@ -14755,7 +15355,8 @@ function XViewScene({
14755
15355
  onMentionClick: handleAddExistingNode,
14756
15356
  onUploadFile: upload_file_action,
14757
15357
  userRole: userPermissionRole,
14758
- currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
15358
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
15359
+ viewMembers
14759
15360
  }
14760
15361
  ),
14761
15362
  detailsNode && !detailsNode.is_quest && /* @__PURE__ */ React25.createElement(
@@ -14868,7 +15469,10 @@ function XViewScene({
14868
15469
  onRenderAncestry: handleStartReadingAncestry,
14869
15470
  onEditAncestry: handleEditAncestry,
14870
15471
  onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
14871
- onFocusNode: handleFocusNode
15472
+ onFocusNode: handleFocusNode,
15473
+ viewMembers,
15474
+ currentUser: session == null ? void 0 : session.user,
15475
+ onStartQuest: handleStartQuestQuick
14872
15476
  }
14873
15477
  ),
14874
15478
  /* @__PURE__ */ React25.createElement(
@@ -14941,6 +15545,83 @@ function XViewScene({
14941
15545
  currentViewName: viewParams == null ? void 0 : viewParams.name,
14942
15546
  currentAncestries: ancestryDataRef.current || []
14943
15547
  }
15548
+ ),
15549
+ invalidTargetError && /* @__PURE__ */ React25.createElement(
15550
+ "div",
15551
+ {
15552
+ className: "ui-overlay",
15553
+ style: {
15554
+ position: "fixed",
15555
+ top: "24px",
15556
+ left: "50%",
15557
+ transform: "translateX(-50%)",
15558
+ zIndex: 1e4,
15559
+ padding: "16px 24px",
15560
+ background: "rgba(30, 20, 20, 0.85)",
15561
+ backdropFilter: "blur(12px)",
15562
+ WebkitBackdropFilter: "blur(12px)",
15563
+ border: "1px solid rgba(255, 70, 70, 0.35)",
15564
+ borderRadius: "16px",
15565
+ boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
15566
+ color: "#ffa0a0",
15567
+ display: "flex",
15568
+ alignItems: "center",
15569
+ gap: "16px",
15570
+ fontFamily: "Inter, sans-serif",
15571
+ animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
15572
+ }
15573
+ },
15574
+ /* @__PURE__ */ React25.createElement("style", null, `
15575
+ @keyframes fadeInDown {
15576
+ from { opacity: 0; transform: translate(-50%, -20px); }
15577
+ to { opacity: 1; transform: translate(-50%, 0); }
15578
+ }
15579
+ `),
15580
+ /* @__PURE__ */ React25.createElement(
15581
+ "svg",
15582
+ {
15583
+ width: "20",
15584
+ height: "20",
15585
+ viewBox: "0 0 24 24",
15586
+ fill: "none",
15587
+ stroke: "currentColor",
15588
+ strokeWidth: "2",
15589
+ strokeLinecap: "round",
15590
+ strokeLinejoin: "round"
15591
+ },
15592
+ /* @__PURE__ */ React25.createElement("circle", { cx: "12", cy: "12", r: "10" }),
15593
+ /* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
15594
+ /* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
15595
+ ),
15596
+ /* @__PURE__ */ React25.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
15597
+ /* @__PURE__ */ React25.createElement(
15598
+ "button",
15599
+ {
15600
+ onClick: () => setInvalidTargetError(null),
15601
+ style: {
15602
+ background: "rgba(255, 255, 255, 0.1)",
15603
+ border: "none",
15604
+ color: "white",
15605
+ padding: "4px 10px",
15606
+ borderRadius: "8px",
15607
+ cursor: "pointer",
15608
+ fontSize: "12px",
15609
+ fontWeight: 600,
15610
+ transition: "all 0.2s",
15611
+ marginLeft: "8px",
15612
+ border: "1px solid rgba(255,255,255,0.1)"
15613
+ },
15614
+ onMouseOver: (e) => {
15615
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
15616
+ e.currentTarget.style.transform = "translateY(-1px)";
15617
+ },
15618
+ onMouseOut: (e) => {
15619
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
15620
+ e.currentTarget.style.transform = "translateY(0)";
15621
+ }
15622
+ },
15623
+ "Fechar"
15624
+ )
14944
15625
  )
14945
15626
  );
14946
15627
  }
@@ -15367,6 +16048,17 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
15367
16048
  return { success: false, error: error.message };
15368
16049
  }
15369
16050
  }
16051
+ async function get_view_members_logic(db_services, ownerId, type, itemId) {
16052
+ try {
16053
+ if (!db_services.get_view_members) {
16054
+ return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
16055
+ }
16056
+ return await db_services.get_view_members(ownerId, type, itemId);
16057
+ } catch (error) {
16058
+ console.error("Erro em get_view_members_logic:", error);
16059
+ return { success: false, error: error.message };
16060
+ }
16061
+ }
15370
16062
  export {
15371
16063
  XViewScene,
15372
16064
  delete_uploaded_file_logic,
@@ -15374,6 +16066,7 @@ export {
15374
16066
  get_ancestry_file_logic,
15375
16067
  get_scene_view_data_logic,
15376
16068
  get_single_parent_file_logic,
16069
+ get_view_members_logic,
15377
16070
  import_parent_file_modal_get_logic,
15378
16071
  save_ancestry_board_logic,
15379
16072
  save_view_data_logic,