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

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 +832 -155
  2. package/dist/index.mjs +836 -160
  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",
@@ -2776,18 +2919,27 @@ function createNewCustomProperty(existingProps = []) {
2776
2919
  };
2777
2920
  }
2778
2921
  var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
2779
- const match = refString.match(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/);
2922
+ const match = refString.match(
2923
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
2924
+ );
2780
2925
  if (!match) return null;
2781
2926
  const [_, type, itemId, sectionId] = match;
2782
2927
  let sourceItem = null;
2783
2928
  if (type === "node") {
2784
2929
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
2785
2930
  } else {
2786
- sourceItem = availableAncestries.find((a) => String(a.ancestry_id) === String(itemId));
2931
+ sourceItem = availableAncestries.find(
2932
+ (a) => String(a.ancestry_id) === String(itemId)
2933
+ );
2787
2934
  }
2788
2935
  if (!sourceItem) return null;
2789
- const sections = parseDescriptionSections(sourceItem.description, sourceItem.description_sections);
2790
- const targetSection = sections.find((s) => String(s.id) === String(sectionId));
2936
+ const sections = parseDescriptionSections(
2937
+ sourceItem.description,
2938
+ sourceItem.description_sections
2939
+ );
2940
+ const targetSection = sections.find(
2941
+ (s) => String(s.id) === String(sectionId)
2942
+ );
2791
2943
  if (!targetSection) return null;
2792
2944
  return {
2793
2945
  content: targetSection.content,
@@ -2801,21 +2953,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2801
2953
  let text = rawText;
2802
2954
  const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
2803
2955
  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;
2956
+ text = text.replace(
2957
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
2958
+ (match, type, itemId, sectionId) => {
2959
+ const resolved = resolveDescriptionReference(
2960
+ match,
2961
+ allNodes,
2962
+ allAncestries
2963
+ );
2964
+ if (resolved && !resolved.error) {
2965
+ return resolved.content;
2966
+ }
2967
+ return "[Refer\xEAncia indispon\xEDvel]";
2808
2968
  }
2809
- return "[Refer\xEAncia indispon\xEDvel]";
2810
- });
2969
+ );
2811
2970
  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
- });
2971
+ text = text.replace(
2972
+ /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
2973
+ (match, nodeId) => {
2974
+ const node = allNodes.find((n) => String(n.id) === String(nodeId));
2975
+ return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2976
+ }
2977
+ );
2978
+ text = text.replace(
2979
+ /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
2980
+ (match, url, name) => {
2981
+ return name ? `[Imagem: ${name}]` : `[Imagem]`;
2982
+ }
2983
+ );
2819
2984
  text = text.replace(/^#+\s*/gm, "");
2820
2985
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
2821
2986
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
@@ -2823,7 +2988,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2823
2988
  return text.trim();
2824
2989
  }
2825
2990
  function generateTooltipHtml(data, parentData, ancestryData) {
2826
- const ignoredKeys = ["id", "name", "type", "color", "_baseEmissiveIntensity", "description"];
2991
+ const ignoredKeys = [
2992
+ "id",
2993
+ "name",
2994
+ "type",
2995
+ "color",
2996
+ "_baseEmissiveIntensity",
2997
+ "description"
2998
+ ];
2827
2999
  const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
2828
3000
  const extras = customKeys.length;
2829
3001
  let typeDisplay = "Node";
@@ -2838,7 +3010,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
2838
3010
  <div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
2839
3011
  <div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
2840
3012
  if (data.description) {
2841
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3013
+ const cleanDesc = formatDescriptionForTooltip(
3014
+ data.description,
3015
+ parentData,
3016
+ ancestryData
3017
+ );
2842
3018
  if (cleanDesc) {
2843
3019
  html += `<div style="
2844
3020
  margin-top: 6px;
@@ -2876,7 +3052,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
2876
3052
  html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
2877
3053
  }
2878
3054
  if (hasDescription) {
2879
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3055
+ const cleanDesc = formatDescriptionForTooltip(
3056
+ data.description,
3057
+ parentData,
3058
+ ancestryData
3059
+ );
2880
3060
  if (cleanDesc) {
2881
3061
  html += `<div style="
2882
3062
  display: -webkit-box;
@@ -2958,7 +3138,8 @@ var IGNORED_KEYS = [
2958
3138
  "isAddingAbstractionNodes",
2959
3139
  "status",
2960
3140
  "is_quest",
2961
- "raw_title"
3141
+ "raw_title",
3142
+ "assignee_id"
2962
3143
  ];
2963
3144
  function extractCustomPropsFromNode(node) {
2964
3145
  const customPropTypes = node._customPropTypes || {};
@@ -2968,16 +3149,40 @@ function extractCustomPropsFromNode(node) {
2968
3149
  if (t === "date") {
2969
3150
  if (val && typeof val === "object") {
2970
3151
  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 || "" } };
3152
+ return {
3153
+ id: v4_default(),
3154
+ key,
3155
+ type: "date",
3156
+ value: {
3157
+ type: "Date Interval",
3158
+ start: val.date_interval.start || "",
3159
+ end: val.date_interval.end || ""
3160
+ }
3161
+ };
2972
3162
  }
2973
3163
  if ("year" in val) {
2974
- return { id: v4_default(), key, type: "date", value: { type: "Ano", value: val.year || "" } };
3164
+ return {
3165
+ id: v4_default(),
3166
+ key,
3167
+ type: "date",
3168
+ value: { type: "Ano", value: val.year || "" }
3169
+ };
2975
3170
  }
2976
3171
  if ("date" in val) {
2977
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: val.date || "" } };
3172
+ return {
3173
+ id: v4_default(),
3174
+ key,
3175
+ type: "date",
3176
+ value: { type: "Data", value: val.date || "" }
3177
+ };
2978
3178
  }
2979
3179
  }
2980
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: "" } };
3180
+ return {
3181
+ id: v4_default(),
3182
+ key,
3183
+ type: "date",
3184
+ value: { type: "Data", value: "" }
3185
+ };
2981
3186
  }
2982
3187
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
2983
3188
  const safeList = (val || []).map((item) => ({
@@ -2987,7 +3192,12 @@ function extractCustomPropsFromNode(node) {
2987
3192
  }));
2988
3193
  return { id: v4_default(), key, type: t, value: safeList };
2989
3194
  }
2990
- return { id: v4_default(), key, type: t, value: t === "number" ? Number(val) || 0 : String(val ?? "") };
3195
+ return {
3196
+ id: v4_default(),
3197
+ key,
3198
+ type: t,
3199
+ value: t === "number" ? Number(val) || 0 : String(val ?? "")
3200
+ };
2991
3201
  });
2992
3202
  }
2993
3203
  function toObjectFromCustomProps(customProps) {
@@ -3002,7 +3212,12 @@ function toObjectFromCustomProps(customProps) {
3002
3212
  const { type: uiDateType, ...dateValues } = p.value || {};
3003
3213
  if (uiDateType) {
3004
3214
  if (uiDateType === "Date Interval") {
3005
- out[key] = { date_interval: { start: dateValues.start || "", end: dateValues.end || "" } };
3215
+ out[key] = {
3216
+ date_interval: {
3217
+ start: dateValues.start || "",
3218
+ end: dateValues.end || ""
3219
+ }
3220
+ };
3006
3221
  } else if (uiDateType === "Ano") {
3007
3222
  out[key] = { year: dateValues.value || "" };
3008
3223
  } else {
@@ -3038,10 +3253,20 @@ function createTextSprite(text, fontSize = 64) {
3038
3253
  context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
3039
3254
  const texture = new THREE2.CanvasTexture(canvas);
3040
3255
  texture.colorSpace = THREE2.SRGBColorSpace;
3041
- const spriteMaterial = new THREE2.SpriteMaterial({ map: texture, sizeAttenuation: true, depthWrite: false, transparent: true, toneMapped: false });
3256
+ const spriteMaterial = new THREE2.SpriteMaterial({
3257
+ map: texture,
3258
+ sizeAttenuation: true,
3259
+ depthWrite: false,
3260
+ transparent: true,
3261
+ toneMapped: false
3262
+ });
3042
3263
  const sprite = new THREE2.Sprite(spriteMaterial);
3043
3264
  const scaleFactor = 0.05;
3044
- sprite.scale.set(canvas.width * scaleFactor, canvas.height * scaleFactor, 1);
3265
+ sprite.scale.set(
3266
+ canvas.width * scaleFactor,
3267
+ canvas.height * scaleFactor,
3268
+ 1
3269
+ );
3045
3270
  sprite.layers.set(DEFAULT_LAYER);
3046
3271
  return sprite;
3047
3272
  }
@@ -3057,7 +3282,14 @@ function makeGlowTexture() {
3057
3282
  const canvas = document.createElement("canvas");
3058
3283
  canvas.width = canvas.height = size;
3059
3284
  const ctx = canvas.getContext("2d");
3060
- const grd = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
3285
+ const grd = ctx.createRadialGradient(
3286
+ size / 2,
3287
+ size / 2,
3288
+ 0,
3289
+ size / 2,
3290
+ size / 2,
3291
+ size / 2
3292
+ );
3061
3293
  grd.addColorStop(0, "rgba(255,255,255,1)");
3062
3294
  grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
3063
3295
  grd.addColorStop(1, "rgba(255,255,255,0)");
@@ -3073,8 +3305,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
3073
3305
  let midBoost = 1 - Math.abs(L - 0.5) * 2;
3074
3306
  midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
3075
3307
  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 });
3308
+ const auraColor = new THREE2.Color(hexColor).lerp(
3309
+ new THREE2.Color("#ffffff"),
3310
+ 0.25
3311
+ );
3312
+ const auraMat = new THREE2.SpriteMaterial({
3313
+ map: glowTexture,
3314
+ color: auraColor,
3315
+ opacity: auraOpacity,
3316
+ blending: THREE2.AdditiveBlending,
3317
+ transparent: true,
3318
+ depthWrite: false,
3319
+ toneMapped: false
3320
+ });
3078
3321
  const aura = new THREE2.Sprite(auraMat);
3079
3322
  aura.scale.setScalar(auraScale);
3080
3323
  aura.name = "aura";
@@ -3107,7 +3350,17 @@ function calculateNodePositions(nodes) {
3107
3350
  }
3108
3351
  return positions;
3109
3352
  }
3110
- function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3353
+ function updateTooltip({
3354
+ tooltipEl,
3355
+ hoveredNode,
3356
+ hoveredLink,
3357
+ camera,
3358
+ mountEl,
3359
+ isSceneBusy,
3360
+ parentData,
3361
+ ancestryData
3362
+ }) {
3363
+ var _a, _b;
3111
3364
  if (!tooltipEl || !camera || !mountEl) return;
3112
3365
  let content = "";
3113
3366
  let positionTarget = null;
@@ -3117,20 +3370,35 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3117
3370
  currentId = `node_${hoveredNode.userData.id}`;
3118
3371
  positionTarget = hoveredNode;
3119
3372
  if (tooltipEl.dataset.currentId !== currentId) {
3120
- content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3373
+ content = generateTooltipHtml(
3374
+ hoveredNode.userData,
3375
+ parentData,
3376
+ ancestryData
3377
+ );
3121
3378
  }
3122
3379
  } 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);
3380
+ const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
3381
+ const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
3382
+ if (hasContent) {
3383
+ currentId = `link_${hoveredLink.userData.id}`;
3384
+ if (hoveredLink.userData.isCurved) {
3385
+ const positions = hoveredLink.geometry.attributes.position.array;
3386
+ const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3387
+ positionTarget = new THREE2.Vector3(
3388
+ positions[midIndex],
3389
+ positions[midIndex + 1],
3390
+ positions[midIndex + 2]
3391
+ );
3392
+ } else {
3393
+ positionTarget = new THREE2.Vector3().addVectors(
3394
+ hoveredLink.userData.sourceNode.position,
3395
+ hoveredLink.userData.targetNode.position
3396
+ ).multiplyScalar(0.5);
3397
+ }
3398
+ isLink = true;
3399
+ if (tooltipEl.dataset.currentId !== currentId) {
3400
+ content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
3401
+ }
3134
3402
  }
3135
3403
  }
3136
3404
  if (positionTarget) {
@@ -3154,9 +3422,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3154
3422
  const tooltipRect = tooltipEl.getBoundingClientRect();
3155
3423
  const offsetX = 20;
3156
3424
  const offsetY = 20;
3157
- if (x + tooltipRect.width + offsetX > clientWidth) x = x - tooltipRect.width - offsetX;
3425
+ if (x + tooltipRect.width + offsetX > clientWidth)
3426
+ x = x - tooltipRect.width - offsetX;
3158
3427
  else x = x + offsetX;
3159
- if (y + tooltipRect.height + offsetY > clientHeight) y = y - tooltipRect.height - offsetY;
3428
+ if (y + tooltipRect.height + offsetY > clientHeight)
3429
+ y = y - tooltipRect.height - offsetY;
3160
3430
  else y = y + offsetY;
3161
3431
  tooltipEl.style.display = "block";
3162
3432
  tooltipEl.style.left = `${x}px`;
@@ -3190,7 +3460,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
3190
3460
  const content = parts[i + 2] || "";
3191
3461
  let finalUuid = null;
3192
3462
  if (suffix) {
3193
- const existingMatch = existingSections.find((s) => s.id && s.id.includes(suffix));
3463
+ const existingMatch = existingSections.find(
3464
+ (s) => s.id && s.id.includes(suffix)
3465
+ );
3194
3466
  if (existingMatch) {
3195
3467
  finalUuid = existingMatch.id;
3196
3468
  } else {
@@ -3281,28 +3553,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
3281
3553
  function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3282
3554
  const [width, setWidth] = useState3(initialWidth);
3283
3555
  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]);
3556
+ const handlePointerDown = useCallback(
3557
+ (e) => {
3558
+ e.preventDefault();
3559
+ e.stopPropagation();
3560
+ setIsResizing(true);
3561
+ const startX = e.clientX;
3562
+ const startWidth = width;
3563
+ const originalUserSelect = document.body.style.userSelect;
3564
+ document.body.style.userSelect = "none";
3565
+ const handlePointerMove = (moveEvent) => {
3566
+ const deltaX = startX - moveEvent.clientX;
3567
+ const newWidth = Math.min(
3568
+ Math.max(startWidth + deltaX, minWidth),
3569
+ maxWidth
3570
+ );
3571
+ setWidth(newWidth);
3572
+ };
3573
+ const handlePointerUp = () => {
3574
+ setIsResizing(false);
3575
+ document.body.style.userSelect = originalUserSelect;
3576
+ document.removeEventListener("pointermove", handlePointerMove);
3577
+ document.removeEventListener("pointerup", handlePointerUp);
3578
+ };
3579
+ document.addEventListener("pointermove", handlePointerMove);
3580
+ document.addEventListener("pointerup", handlePointerUp);
3581
+ },
3582
+ [width, minWidth, maxWidth]
3583
+ );
3306
3584
  return { width, isResizing, handlePointerDown, setWidth };
3307
3585
  }
3308
3586
 
@@ -3439,9 +3717,9 @@ function CustomPropertyDisplay({
3439
3717
  };
3440
3718
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
3441
3719
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
3442
- const newValue = [...p.value];
3443
- newValue[j] = { ...newValue[j], [f]: v };
3444
- return { ...p, value: newValue };
3720
+ const newValue2 = [...p.value];
3721
+ newValue2[j] = { ...newValue2[j], [f]: v };
3722
+ return { ...p, value: newValue2 };
3445
3723
  });
3446
3724
  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
3725
  const renderEditView = () => {
@@ -3495,14 +3773,14 @@ function CustomPropertyDisplay({
3495
3773
  const inputClass = `${baseInput} ${noSpinnerClass}`;
3496
3774
  const handleDateTypeChange = (newDateType) => {
3497
3775
  var _a3, _b2, _c2;
3498
- let newValue = { type: newDateType };
3776
+ let newValue2 = { type: newDateType };
3499
3777
  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) || "";
3778
+ newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3779
+ newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3502
3780
  } else {
3503
- newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3781
+ newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3504
3782
  }
3505
- handlePropChange("value", newValue);
3783
+ handlePropChange("value", newValue2);
3506
3784
  };
3507
3785
  const handleDateValueChange = (dateField, dateValue) => {
3508
3786
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -4350,7 +4628,7 @@ ${space}${bullet} `);
4350
4628
  }
4351
4629
  ),
4352
4630
  /* @__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")),
4631
+ /* @__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
4632
  /* @__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
4633
  "button",
4356
4634
  {
@@ -5479,7 +5757,7 @@ function AncestryRelationshipPanel({
5479
5757
  onImageClick: handleImageClickFromText,
5480
5758
  onSaveDescription: handleSaveDescriptionInline
5481
5759
  }
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(
5760
+ ) : /* @__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
5761
  DescriptionDisplay,
5484
5762
  {
5485
5763
  description,
@@ -5801,6 +6079,7 @@ function CreateAncestryPanel({
5801
6079
  } = ancestryMode;
5802
6080
  const [isSaving, setIsSaving] = useState11(false);
5803
6081
  const [isLinkCopied, setIsLinkCopied] = useState11(false);
6082
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState11(false);
5804
6083
  const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = useState11(false);
5805
6084
  const handleCopyLink = (e) => {
5806
6085
  e.stopPropagation();
@@ -5868,12 +6147,14 @@ function CreateAncestryPanel({
5868
6147
  };
5869
6148
  const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
5870
6149
  setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
6150
+ setHasUnsavedChanges(true);
5871
6151
  };
5872
6152
  const handleToggleAddMode = (isAbstraction = false) => {
5873
6153
  if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
5874
6154
  setTargetRenderNodeId(null);
5875
6155
  }
5876
6156
  setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
6157
+ setHasUnsavedChanges(true);
5877
6158
  };
5878
6159
  const handleRemoveNode = useCallback2((pathToRemove, isAbstraction = false) => {
5879
6160
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
@@ -5891,6 +6172,7 @@ function CreateAncestryPanel({
5891
6172
  const indexToRemove = pathToRemove[pathToRemove.length - 1];
5892
6173
  if (currentParent.children && currentParent.children.length > indexToRemove) {
5893
6174
  currentParent.children.splice(indexToRemove, 1);
6175
+ setHasUnsavedChanges(true);
5894
6176
  }
5895
6177
  return { ...prev, [treeKey]: newTree };
5896
6178
  });
@@ -5949,6 +6231,7 @@ function CreateAncestryPanel({
5949
6231
  updateGlobalTree(rootTreeClone);
5950
6232
  }
5951
6233
  setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
6234
+ setHasUnsavedChanges(true);
5952
6235
  } else {
5953
6236
  alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
5954
6237
  }
@@ -6021,6 +6304,7 @@ function CreateAncestryPanel({
6021
6304
  const handleAddProp = () => {
6022
6305
  const newProp = createNewCustomProperty(customProps);
6023
6306
  setCustomProps((p) => [...p, newProp]);
6307
+ setHasUnsavedChanges(true);
6024
6308
  setTimeout(() => {
6025
6309
  var _a;
6026
6310
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -6029,11 +6313,13 @@ function CreateAncestryPanel({
6029
6313
  const handleRemoveProp = (i) => {
6030
6314
  const newProps = customProps.filter((_, idx) => idx !== i);
6031
6315
  setCustomProps(newProps);
6316
+ setHasUnsavedChanges(true);
6032
6317
  };
6033
6318
  const handleUpdateProp = (index, updatedProp) => {
6034
6319
  const newProps = [...customProps];
6035
6320
  newProps[index] = updatedProp;
6036
6321
  setCustomProps(newProps);
6322
+ setHasUnsavedChanges(true);
6037
6323
  };
6038
6324
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
6039
6325
  useEffect10(() => {
@@ -6143,6 +6429,7 @@ function CreateAncestryPanel({
6143
6429
  updateGlobalTree(rootTreeClone);
6144
6430
  setBranchStack([...branchStack]);
6145
6431
  setIsPickerOpen(false);
6432
+ setHasUnsavedChanges(true);
6146
6433
  try {
6147
6434
  setIsSaving(true);
6148
6435
  const rootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6156,6 +6443,7 @@ function CreateAncestryPanel({
6156
6443
  rootExtras
6157
6444
  );
6158
6445
  setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
6446
+ setHasUnsavedChanges(false);
6159
6447
  if (onRenderFullAncestry) {
6160
6448
  const fullTreePayload = {
6161
6449
  ancestry_id: ancestryMode.currentAncestryId || "temp_root",
@@ -6198,6 +6486,7 @@ function CreateAncestryPanel({
6198
6486
  if (branchIndex !== -1) {
6199
6487
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6200
6488
  updateGlobalTree(rootTreeClone);
6489
+ setHasUnsavedChanges(true);
6201
6490
  try {
6202
6491
  setIsSaving(true);
6203
6492
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6219,6 +6508,7 @@ function CreateAncestryPanel({
6219
6508
  isPrivate,
6220
6509
  ancestryMode.abstraction_tree
6221
6510
  ));
6511
+ setHasUnsavedChanges(false);
6222
6512
  if (onClearAncestryVisuals) {
6223
6513
  onClearAncestryVisuals(currentStep.branchId);
6224
6514
  }
@@ -6251,6 +6541,7 @@ function CreateAncestryPanel({
6251
6541
  if (branchIndex !== -1) {
6252
6542
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6253
6543
  updateGlobalTree(rootTreeClone);
6544
+ setHasUnsavedChanges(true);
6254
6545
  try {
6255
6546
  setIsSaving(true);
6256
6547
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6272,6 +6563,7 @@ function CreateAncestryPanel({
6272
6563
  isPrivate,
6273
6564
  ancestryMode.abstraction_tree
6274
6565
  ));
6566
+ setHasUnsavedChanges(false);
6275
6567
  if (onClearAncestryVisuals) {
6276
6568
  onClearAncestryVisuals(currentStep.branchId);
6277
6569
  }
@@ -6533,6 +6825,7 @@ function CreateAncestryPanel({
6533
6825
  }
6534
6826
  setBranchStack(parentStack);
6535
6827
  setTargetScrollSectionId(targetFocusId);
6828
+ setHasUnsavedChanges(true);
6536
6829
  if (onRenderFullAncestry) {
6537
6830
  const parentStack2 = currentStack;
6538
6831
  const rotation = parentStack2.reduce((acc, step) => {
@@ -6594,7 +6887,6 @@ function CreateAncestryPanel({
6594
6887
  direction,
6595
6888
  tree: {
6596
6889
  node: nodeData,
6597
- node_id: nodeId,
6598
6890
  children: [],
6599
6891
  relationship: {}
6600
6892
  }
@@ -6616,6 +6908,7 @@ function CreateAncestryPanel({
6616
6908
  savedMaxIndex: parentIndexToSave,
6617
6909
  entryDirection: direction
6618
6910
  }]);
6911
+ setHasUnsavedChanges(true);
6619
6912
  if (branch && branch.tree && onRenderFullAncestry) {
6620
6913
  const branchAncestryObj = {
6621
6914
  ancestry_id: branch.id,
@@ -6666,6 +6959,10 @@ function CreateAncestryPanel({
6666
6959
  const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
6667
6960
  const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
6668
6961
  const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
6962
+ if (!keepOpen && !hasUnsavedChanges) {
6963
+ onClose();
6964
+ return;
6965
+ }
6669
6966
  if (!currentInputName.trim()) {
6670
6967
  alert("O nome n\xE3o pode estar vazio.");
6671
6968
  return;
@@ -6673,11 +6970,7 @@ function CreateAncestryPanel({
6673
6970
  setIsSaving(true);
6674
6971
  const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
6675
6972
  setExistingSections(processedSections);
6676
- const updatedRootTree = applyDescriptionToTree(
6677
- ancestryMode.tree,
6678
- currentInputDesc,
6679
- processedSections
6680
- );
6973
+ const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
6681
6974
  const extrasObj = {
6682
6975
  ...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
6683
6976
  is_private: isPrivate
@@ -6719,6 +7012,7 @@ function CreateAncestryPanel({
6719
7012
  isPrivate,
6720
7013
  ancestryMode.abstraction_tree
6721
7014
  ));
7015
+ setHasUnsavedChanges(false);
6722
7016
  if (onRenderFullAncestry) {
6723
7017
  const rotation = branchStack.reduce((acc, step) => {
6724
7018
  return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
@@ -6770,6 +7064,7 @@ function CreateAncestryPanel({
6770
7064
  updatedRootTree,
6771
7065
  extrasObj
6772
7066
  );
7067
+ setHasUnsavedChanges(false);
6773
7068
  setLastSavedSnapshot(takeSnapshot(
6774
7069
  updatedRootTree,
6775
7070
  currentInputName,
@@ -6792,6 +7087,7 @@ function CreateAncestryPanel({
6792
7087
  const newTreeString = JSON.stringify(newRootTree);
6793
7088
  if (currentTreeString !== newTreeString) {
6794
7089
  updateGlobalTree(newRootTree);
7090
+ setHasUnsavedChanges(true);
6795
7091
  }
6796
7092
  }, [description, existingSections]);
6797
7093
  const handleTriggerFullRender = () => {
@@ -6814,6 +7110,7 @@ function CreateAncestryPanel({
6814
7110
  };
6815
7111
  const handleSaveDescriptionInline = (newDesc) => {
6816
7112
  setDescription(newDesc);
7113
+ setHasUnsavedChanges(true);
6817
7114
  handleLocalSave(true, { description: newDesc });
6818
7115
  };
6819
7116
  const swallow = (e) => e.stopPropagation();
@@ -6943,7 +7240,11 @@ function CreateAncestryPanel({
6943
7240
  {
6944
7241
  type: "text",
6945
7242
  value: ancestryName,
6946
- onChange: (e) => setAncestryName(e.target.value),
7243
+ onChange: (e) => {
7244
+ setAncestryName(e.target.value);
7245
+ setHasUnsavedChanges(true);
7246
+ },
7247
+ readOnly: isContextLinked,
6947
7248
  placeholder: "Nome da Ancestralidade",
6948
7249
  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
7250
  }
@@ -7431,7 +7732,7 @@ function ColorPicker({ color, onChange, disabled }) {
7431
7732
  style: { backgroundColor: preset },
7432
7733
  title: preset
7433
7734
  },
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 })
7735
+ (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
7736
  )))), /* @__PURE__ */ React12.createElement("style", null, `
7436
7737
  .custom-react-colorful .react-colorful {
7437
7738
  width: 100%;
@@ -7523,13 +7824,23 @@ function InSceneCreationForm({
7523
7824
  }, [hasImages, useImageAsTexture, onImageChange]);
7524
7825
  useEffect13(() => {
7525
7826
  let result = [];
7827
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
7526
7828
  if (typeInput.trim() === "") {
7527
- result = existingTypes.filter((t) => !types.includes(t));
7829
+ result = validExistingTypes.filter((t) => !types.includes(t));
7528
7830
  } else {
7529
- const lowercasedInput = typeInput.toLowerCase();
7530
- result = existingTypes.filter(
7531
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
7532
- );
7831
+ console.log("InSceneCreationForm: Filtrando tipos com input:", typeInput);
7832
+ try {
7833
+ const lowercasedInput = typeInput.toLowerCase();
7834
+ result = validExistingTypes.filter((t) => {
7835
+ if (!t) {
7836
+ console.warn("InSceneCreationForm: Tipo encontrado como undefined/null durante filtragem");
7837
+ return false;
7838
+ }
7839
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
7840
+ });
7841
+ } catch (err) {
7842
+ console.error("InSceneCreationForm: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
7843
+ }
7533
7844
  }
7534
7845
  if (sourceTypes) {
7535
7846
  const priorityTypes = Array.isArray(sourceTypes) ? sourceTypes : [sourceTypes];
@@ -7592,9 +7903,9 @@ function InSceneCreationForm({
7592
7903
  };
7593
7904
  const handleToggleImageMode = () => {
7594
7905
  var _a2, _b;
7595
- const newValue = !useImageAsTexture;
7596
- setUseImageAsTexture(newValue);
7597
- if (newValue) {
7906
+ const newValue2 = !useImageAsTexture;
7907
+ setUseImageAsTexture(newValue2);
7908
+ if (newValue2) {
7598
7909
  const firstImageProp = customProps.find((p) => p.type === "images");
7599
7910
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
7600
7911
  const url = firstImageProp.value[0].value;
@@ -7892,9 +8203,9 @@ function InSceneVersionForm({
7892
8203
  };
7893
8204
  const handleToggleImageMode = () => {
7894
8205
  var _a, _b;
7895
- const newValue = !useImageAsTexture;
7896
- setUseImageAsTexture(newValue);
7897
- if (newValue) {
8206
+ const newValue2 = !useImageAsTexture;
8207
+ setUseImageAsTexture(newValue2);
8208
+ if (newValue2) {
7898
8209
  const firstImageProp = customProps.find((p) => p.type === "images");
7899
8210
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
7900
8211
  const url = firstImageProp.value[0].value;
@@ -8048,7 +8359,7 @@ function InSceneVersionForm({
8048
8359
 
8049
8360
  // src/components/InSceneQuestForm.jsx
8050
8361
  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";
8362
+ 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
8363
  var QUEST_STATUS_COLORS2 = {
8053
8364
  "Backlog": "#64748b",
8054
8365
  "In Progress": "#eab308",
@@ -8070,10 +8381,13 @@ function InSceneQuestForm({
8070
8381
  onSizeChange,
8071
8382
  viewName = "Projeto",
8072
8383
  // NOVA PROP
8073
- questCounter = 1
8384
+ questCounter = 1,
8074
8385
  // NOVA PROP
8386
+ viewMembers = []
8075
8387
  }) {
8388
+ var _a, _b;
8076
8389
  const [name, setName] = useState16("");
8390
+ const [assigneeId, setAssigneeId] = useState16("");
8077
8391
  const [types, setTypes] = useState16(["quest"]);
8078
8392
  const [typeInput, setTypeInput] = useState16("");
8079
8393
  const [status, setStatus] = useState16("Backlog");
@@ -8081,6 +8395,8 @@ function InSceneQuestForm({
8081
8395
  const [intensity, setIntensity] = useState16(0);
8082
8396
  const [description, setDescription] = useState16("");
8083
8397
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState16(false);
8398
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState16(false);
8399
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = useState16("");
8084
8400
  const [customProps, setCustomProps] = useState16([]);
8085
8401
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
8086
8402
  const propsEndRef = useRef12(null);
@@ -8089,8 +8405,8 @@ function InSceneQuestForm({
8089
8405
  const newProp = createNewCustomProperty(customProps);
8090
8406
  setCustomProps([...customProps, newProp]);
8091
8407
  setTimeout(() => {
8092
- var _a;
8093
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
8408
+ var _a2;
8409
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
8094
8410
  }, 100);
8095
8411
  };
8096
8412
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -8136,6 +8452,7 @@ function InSceneQuestForm({
8136
8452
  type: types,
8137
8453
  color: QUEST_STATUS_COLORS2[status],
8138
8454
  status,
8455
+ assignee_id: assigneeId || null,
8139
8456
  size,
8140
8457
  intensity,
8141
8458
  description: description.trim(),
@@ -8207,7 +8524,59 @@ function InSceneQuestForm({
8207
8524
  },
8208
8525
  /* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
8209
8526
  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(
8527
+ )))))), /* @__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(
8528
+ "button",
8529
+ {
8530
+ type: "button",
8531
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
8532
+ 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"
8533
+ },
8534
+ /* @__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")),
8535
+ /* @__PURE__ */ React15.createElement(FiChevronDown5, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
8536
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
8537
+ setIsAssigneeDropdownOpen(false);
8538
+ setAssigneeSearchQuery("");
8539
+ } }), /* @__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(
8540
+ "input",
8541
+ {
8542
+ type: "text",
8543
+ autoFocus: true,
8544
+ placeholder: "Buscar membro...",
8545
+ value: assigneeSearchQuery,
8546
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
8547
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
8548
+ onClick: (e) => e.stopPropagation()
8549
+ }
8550
+ )), /* @__PURE__ */ React15.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React15.createElement(
8551
+ "li",
8552
+ {
8553
+ onClick: () => {
8554
+ setAssigneeId("");
8555
+ setIsAssigneeDropdownOpen(false);
8556
+ setAssigneeSearchQuery("");
8557
+ },
8558
+ 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"}`
8559
+ },
8560
+ "Nenhum"
8561
+ ), viewMembers.filter((member) => {
8562
+ const search = assigneeSearchQuery.toLowerCase();
8563
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8564
+ }).map((member) => /* @__PURE__ */ React15.createElement(
8565
+ "li",
8566
+ {
8567
+ key: member.id,
8568
+ onClick: () => {
8569
+ setAssigneeId(member.id);
8570
+ setIsAssigneeDropdownOpen(false);
8571
+ setAssigneeSearchQuery("");
8572
+ },
8573
+ 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"}`
8574
+ },
8575
+ member.name || member.email || member.id
8576
+ )), viewMembers.filter((member) => {
8577
+ const search = assigneeSearchQuery.toLowerCase();
8578
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8579
+ }).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
8580
  "input",
8212
8581
  {
8213
8582
  type: "text",
@@ -8314,6 +8683,7 @@ function NodeDetailsPanel({
8314
8683
  return !!(node == null ? void 0 : node.useImageAsTexture);
8315
8684
  });
8316
8685
  const [selectedImageUrl, setSelectedImageUrl] = useState17((node == null ? void 0 : node.textureImageUrl) ?? null);
8686
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState17(false);
8317
8687
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8318
8688
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8319
8689
  initialWidth: isReadMode ? 700 : 440,
@@ -8351,6 +8721,7 @@ function NodeDetailsPanel({
8351
8721
  else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
8352
8722
  else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
8353
8723
  setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
8724
+ setHasUnsavedChanges(false);
8354
8725
  }
8355
8726
  }, [node]);
8356
8727
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
@@ -8361,15 +8732,22 @@ function NodeDetailsPanel({
8361
8732
  }
8362
8733
  }, [hasImages, useImageAsTexture]);
8363
8734
  useEffect15(() => {
8735
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8364
8736
  if (typeInput.trim() === "") {
8365
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
8737
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8366
8738
  } else {
8367
- const lowercasedInput = typeInput.toLowerCase();
8368
- setFilteredTypes(
8369
- existingTypes.filter(
8370
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8371
- )
8372
- );
8739
+ console.log("NodeDetailsPanel: Filtrando tipos com input:", typeInput);
8740
+ try {
8741
+ const lowercasedInput = typeInput.toLowerCase();
8742
+ setFilteredTypes(
8743
+ validExistingTypes.filter((t) => {
8744
+ if (!t) return false;
8745
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
8746
+ })
8747
+ );
8748
+ } catch (err) {
8749
+ console.error("NodeDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
8750
+ }
8373
8751
  }
8374
8752
  }, [typeInput, existingTypes, types]);
8375
8753
  const handleIntensityChangeLocal = (e) => {
@@ -8377,6 +8755,7 @@ function NodeDetailsPanel({
8377
8755
  setIntensity(val);
8378
8756
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
8379
8757
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
8758
+ setHasUnsavedChanges(true);
8380
8759
  };
8381
8760
  const handleCopyLink = () => {
8382
8761
  if (!(node == null ? void 0 : node.id)) return;
@@ -8394,14 +8773,17 @@ function NodeDetailsPanel({
8394
8773
  const v = e.target.value;
8395
8774
  setName(v);
8396
8775
  onNameChange == null ? void 0 : onNameChange(node.id, v);
8776
+ setHasUnsavedChanges(true);
8397
8777
  };
8398
8778
  const handleColorChange = (val) => {
8399
8779
  setColor(val);
8400
8780
  onColorChange == null ? void 0 : onColorChange(node.id, val);
8781
+ setHasUnsavedChanges(true);
8401
8782
  };
8402
8783
  const handleSizeChange = (newSize) => {
8403
8784
  setSize(newSize);
8404
8785
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
8786
+ setHasUnsavedChanges(true);
8405
8787
  };
8406
8788
  const handleAddType = (newType) => {
8407
8789
  const trimmed = newType.trim();
@@ -8409,10 +8791,12 @@ function NodeDetailsPanel({
8409
8791
  setTypes([...types, trimmed]);
8410
8792
  setTypeInput("");
8411
8793
  setShowTypeSuggestions(false);
8794
+ setHasUnsavedChanges(true);
8412
8795
  }
8413
8796
  };
8414
8797
  const handleRemoveType = (indexToRemove) => {
8415
8798
  setTypes(types.filter((_, index) => index !== indexToRemove));
8799
+ setHasUnsavedChanges(true);
8416
8800
  };
8417
8801
  const handleTypeInputKeyDown = (e) => {
8418
8802
  if (e.key === "Enter") {
@@ -8425,6 +8809,7 @@ function NodeDetailsPanel({
8425
8809
  const handleAddProp = () => {
8426
8810
  const newProp = createNewCustomProperty(customProps);
8427
8811
  setCustomProps((p) => [...p, newProp]);
8812
+ setHasUnsavedChanges(true);
8428
8813
  setTimeout(() => {
8429
8814
  var _a;
8430
8815
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8433,19 +8818,21 @@ function NodeDetailsPanel({
8433
8818
  const handleRemoveProp = (i) => {
8434
8819
  const newProps = customProps.filter((_, idx) => idx !== i);
8435
8820
  setCustomProps(newProps);
8821
+ setHasUnsavedChanges(true);
8436
8822
  triggerAutoSave({ customProps: newProps });
8437
8823
  };
8438
8824
  const handleUpdateProp = (index, updatedProp) => {
8439
8825
  const newProps = [...customProps];
8440
8826
  newProps[index] = updatedProp;
8441
8827
  setCustomProps(newProps);
8828
+ setHasUnsavedChanges(true);
8442
8829
  if (!updatedProp.isEditing) {
8443
8830
  triggerAutoSave({ customProps: newProps });
8444
8831
  }
8445
8832
  };
8446
8833
  const handleToggleImageMode = () => {
8447
- const newValue = !useImageAsTexture;
8448
8834
  setUseImageAsTexture(newValue);
8835
+ setHasUnsavedChanges(true);
8449
8836
  let activeUrl = null;
8450
8837
  if (newValue) {
8451
8838
  const firstImageProp = customProps.find((p) => p.type === "images");
@@ -8467,6 +8854,7 @@ function NodeDetailsPanel({
8467
8854
  };
8468
8855
  const handleSelectTexture = (url) => {
8469
8856
  setSelectedImageUrl(url);
8857
+ setHasUnsavedChanges(true);
8470
8858
  onImageChange == null ? void 0 : onImageChange(true, url, color);
8471
8859
  onDataUpdate == null ? void 0 : onDataUpdate({
8472
8860
  ...node,
@@ -8477,6 +8865,7 @@ function NodeDetailsPanel({
8477
8865
  };
8478
8866
  const handleSaveDescriptionInline = (newDescription) => {
8479
8867
  setDescription(newDescription);
8868
+ setHasUnsavedChanges(true);
8480
8869
  onDataUpdate({ ...node, description: newDescription });
8481
8870
  triggerAutoSave({ description: newDescription });
8482
8871
  };
@@ -8487,6 +8876,10 @@ function NodeDetailsPanel({
8487
8876
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8488
8877
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8489
8878
  const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
8879
+ if (!keepOpen && !hasUnsavedChanges) {
8880
+ onClose();
8881
+ return;
8882
+ }
8490
8883
  if (!currentName.trim() || currentTypes.length === 0) {
8491
8884
  alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8492
8885
  return;
@@ -8511,6 +8904,7 @@ function NodeDetailsPanel({
8511
8904
  };
8512
8905
  await onSave(dataToSave, keepOpen);
8513
8906
  onDataUpdate(dataToSave);
8907
+ setHasUnsavedChanges(false);
8514
8908
  if (!keepOpen) {
8515
8909
  onClose();
8516
8910
  }
@@ -8577,7 +8971,7 @@ function NodeDetailsPanel({
8577
8971
  onImageClick: handleImageClickFromText,
8578
8972
  onSaveDescription: handleSaveDescriptionInline
8579
8973
  }
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(
8974
+ ) : /* @__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
8975
  "button",
8582
8976
  {
8583
8977
  onClick: handleCopyLink,
@@ -8585,7 +8979,7 @@ function NodeDetailsPanel({
8585
8979
  title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
8586
8980
  },
8587
8981
  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(
8982
+ )), /* @__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
8983
  "button",
8590
8984
  {
8591
8985
  type: "button",
@@ -8747,6 +9141,7 @@ function NodeDetailsPanel({
8747
9141
  initialValue: description,
8748
9142
  onSave: (newDescription) => {
8749
9143
  setDescription(newDescription);
9144
+ setHasUnsavedChanges(true);
8750
9145
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
8751
9146
  triggerAutoSave({ description: newDescription });
8752
9147
  },
@@ -8760,7 +9155,7 @@ function NodeDetailsPanel({
8760
9155
 
8761
9156
  // src/components/QuestDetailsPanel.jsx
8762
9157
  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";
9158
+ 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
9159
  var QUEST_STATUS_COLORS3 = {
8765
9160
  "Backlog": "#64748b",
8766
9161
  "In Progress": "#eab308",
@@ -8783,7 +9178,8 @@ function QuestDetailsPanel({
8783
9178
  onMentionClick,
8784
9179
  onUploadFile,
8785
9180
  userRole,
8786
- currentDatasetName
9181
+ currentDatasetName,
9182
+ viewMembers = []
8787
9183
  }) {
8788
9184
  var _a;
8789
9185
  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 +9191,12 @@ function QuestDetailsPanel({
8795
9191
  const [typeInput, setTypeInput] = useState18("");
8796
9192
  const [status, setStatus] = useState18((node == null ? void 0 : node.status) ?? "Backlog");
8797
9193
  const [size, setSize] = useState18((node == null ? void 0 : node.size) ?? "medium");
9194
+ const [assigneeId, setAssigneeId] = useState18((node == null ? void 0 : node.assignee_id) || "");
8798
9195
  const [description, setDescription] = useState18((node == null ? void 0 : node.description) ?? "");
8799
9196
  const [intensity, setIntensity] = useState18((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8800
9197
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState18(false);
9198
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState18(false);
9199
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = useState18("");
8801
9200
  const [customProps, setCustomProps] = useState18(() => extractCustomPropsFromNode(node || {}));
8802
9201
  const [showTypeSuggestions, setShowTypeSuggestions] = useState18(false);
8803
9202
  const [filteredTypes, setFilteredTypes] = useState18([]);
@@ -8806,6 +9205,7 @@ function QuestDetailsPanel({
8806
9205
  const [existingSections, setExistingSections] = useState18((node == null ? void 0 : node.description_sections) || []);
8807
9206
  const [isSaving, setIsSaving] = useState18(false);
8808
9207
  const [isLinkCopied, setIsLinkCopied] = useState18(false);
9208
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState18(false);
8809
9209
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8810
9210
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8811
9211
  initialWidth: isReadMode ? 700 : 440,
@@ -8833,22 +9233,34 @@ function QuestDetailsPanel({
8833
9233
  setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8834
9234
  setStatus((node == null ? void 0 : node.status) ?? "Backlog");
8835
9235
  setSize((node == null ? void 0 : node.size) ?? "medium");
9236
+ setAssigneeId((node == null ? void 0 : node.assignee_id) || "");
8836
9237
  setDescription((node == null ? void 0 : node.description) ?? "");
8837
9238
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8838
9239
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
8839
9240
  setCustomProps(extractCustomPropsFromNode(node || {}));
9241
+ setHasUnsavedChanges(false);
8840
9242
  }
8841
9243
  }, [node]);
8842
9244
  useEffect16(() => {
9245
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8843
9246
  if (typeInput.trim() === "") {
8844
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
9247
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8845
9248
  } else {
8846
- const lowercasedInput = typeInput.toLowerCase();
8847
- setFilteredTypes(
8848
- existingTypes.filter(
8849
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8850
- )
8851
- );
9249
+ console.log("QuestDetailsPanel: Filtrando tipos com input:", typeInput);
9250
+ try {
9251
+ const lowercasedInput = typeInput.toLowerCase();
9252
+ setFilteredTypes(
9253
+ validExistingTypes.filter((t) => {
9254
+ if (!t) {
9255
+ console.warn("QuestDetailsPanel: Tipo encontrado como undefined/null durante filtragem");
9256
+ return false;
9257
+ }
9258
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
9259
+ })
9260
+ );
9261
+ } catch (err) {
9262
+ console.error("QuestDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
9263
+ }
8852
9264
  }
8853
9265
  }, [typeInput, existingTypes, types]);
8854
9266
  const handleCopyLink = () => {
@@ -8868,16 +9280,19 @@ function QuestDetailsPanel({
8868
9280
  setRawTitle(val);
8869
9281
  const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8870
9282
  onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
9283
+ setHasUnsavedChanges(true);
8871
9284
  };
8872
9285
  const handleSizeChange = (newSize) => {
8873
9286
  setSize(newSize);
8874
9287
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
9288
+ setHasUnsavedChanges(true);
8875
9289
  };
8876
9290
  const handleStatusChange = (newStatus) => {
8877
9291
  setStatus(newStatus);
8878
9292
  const newColor = QUEST_STATUS_COLORS3[newStatus];
8879
9293
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
8880
9294
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
9295
+ setHasUnsavedChanges(true);
8881
9296
  };
8882
9297
  const handleAddType = (newType) => {
8883
9298
  const trimmed = newType.trim();
@@ -8885,11 +9300,13 @@ function QuestDetailsPanel({
8885
9300
  setTypes([...types, trimmed]);
8886
9301
  setTypeInput("");
8887
9302
  setShowTypeSuggestions(false);
9303
+ setHasUnsavedChanges(true);
8888
9304
  }
8889
9305
  };
8890
9306
  const handleRemoveType = (indexToRemove) => {
8891
9307
  if (types[indexToRemove] === "quest") return;
8892
9308
  setTypes(types.filter((_, index) => index !== indexToRemove));
9309
+ setHasUnsavedChanges(true);
8893
9310
  };
8894
9311
  const handleTypeInputKeyDown = (e) => {
8895
9312
  if (e.key === "Enter") {
@@ -8902,6 +9319,7 @@ function QuestDetailsPanel({
8902
9319
  const handleAddProp = () => {
8903
9320
  const newProp = createNewCustomProperty(customProps);
8904
9321
  setCustomProps((p) => [...p, newProp]);
9322
+ setHasUnsavedChanges(true);
8905
9323
  setTimeout(() => {
8906
9324
  var _a2;
8907
9325
  (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8910,18 +9328,21 @@ function QuestDetailsPanel({
8910
9328
  const handleRemoveProp = (i) => {
8911
9329
  const newProps = customProps.filter((_, idx) => idx !== i);
8912
9330
  setCustomProps(newProps);
9331
+ setHasUnsavedChanges(true);
8913
9332
  triggerAutoSave({ customProps: newProps });
8914
9333
  };
8915
9334
  const handleUpdateProp = (index, updatedProp) => {
8916
9335
  const newProps = [...customProps];
8917
9336
  newProps[index] = updatedProp;
8918
9337
  setCustomProps(newProps);
9338
+ setHasUnsavedChanges(true);
8919
9339
  if (!updatedProp.isEditing) {
8920
9340
  triggerAutoSave({ customProps: newProps });
8921
9341
  }
8922
9342
  };
8923
9343
  const handleSaveDescriptionInline = (newDescription) => {
8924
9344
  setDescription(newDescription);
9345
+ setHasUnsavedChanges(true);
8925
9346
  onDataUpdate({ ...node, description: newDescription });
8926
9347
  triggerAutoSave({ description: newDescription });
8927
9348
  };
@@ -8929,10 +9350,15 @@ function QuestDetailsPanel({
8929
9350
  const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
8930
9351
  const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
8931
9352
  const currentTypes = overrides.types !== void 0 ? overrides.types : types;
9353
+ const currentAssigneeId = overrides.assigneeId !== void 0 ? overrides.assigneeId : assigneeId;
8932
9354
  const currentDescription = overrides.description !== void 0 ? overrides.description : description;
8933
9355
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8934
9356
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8935
9357
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
9358
+ if (!keepOpen && !hasUnsavedChanges) {
9359
+ onClose();
9360
+ return;
9361
+ }
8936
9362
  if (!currentRawTitle.trim() || currentTypes.length === 0) {
8937
9363
  alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8938
9364
  return;
@@ -8950,6 +9376,7 @@ function QuestDetailsPanel({
8950
9376
  type: currentTypes,
8951
9377
  color: QUEST_STATUS_COLORS3[currentStatus],
8952
9378
  status: currentStatus,
9379
+ assignee_id: currentAssigneeId || null,
8953
9380
  size,
8954
9381
  description: currentDescription,
8955
9382
  description_sections: processedSections,
@@ -8962,6 +9389,7 @@ function QuestDetailsPanel({
8962
9389
  };
8963
9390
  await onSave(dataToSave, keepOpen);
8964
9391
  onDataUpdate(dataToSave);
9392
+ setHasUnsavedChanges(false);
8965
9393
  if (!keepOpen) {
8966
9394
  onClose();
8967
9395
  }
@@ -8981,6 +9409,8 @@ function QuestDetailsPanel({
8981
9409
  onClose();
8982
9410
  };
8983
9411
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
9412
+ const assigneeMember = viewMembers.find((m) => m.id === assigneeId);
9413
+ const isAssigneeUndefined = assigneeId && !assigneeMember;
8984
9414
  return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(
8985
9415
  "div",
8986
9416
  {
@@ -9019,7 +9449,7 @@ function QuestDetailsPanel({
9019
9449
  onImageClick: handleImageClickFromText,
9020
9450
  onSaveDescription: handleSaveDescriptionInline
9021
9451
  }
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(
9452
+ ) : /* @__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
9453
  "input",
9024
9454
  {
9025
9455
  type: "text",
@@ -9050,7 +9480,71 @@ function QuestDetailsPanel({
9050
9480
  },
9051
9481
  /* @__PURE__ */ React17.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS3[s] } }),
9052
9482
  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(
9483
+ )))))), /* @__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(
9484
+ "button",
9485
+ {
9486
+ type: "button",
9487
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9488
+ 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"
9489
+ },
9490
+ /* @__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")),
9491
+ /* @__PURE__ */ React17.createElement(FiChevronDown6, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
9492
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
9493
+ setIsAssigneeDropdownOpen(false);
9494
+ setAssigneeSearchQuery("");
9495
+ } }), /* @__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(
9496
+ "input",
9497
+ {
9498
+ type: "text",
9499
+ autoFocus: true,
9500
+ placeholder: "Buscar membro...",
9501
+ value: assigneeSearchQuery,
9502
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
9503
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9504
+ onClick: (e) => e.stopPropagation()
9505
+ }
9506
+ )), /* @__PURE__ */ React17.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React17.createElement(
9507
+ "li",
9508
+ {
9509
+ onClick: () => {
9510
+ setAssigneeId("");
9511
+ setHasUnsavedChanges(true);
9512
+ setIsAssigneeDropdownOpen(false);
9513
+ setAssigneeSearchQuery("");
9514
+ },
9515
+ 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"}`
9516
+ },
9517
+ "Nenhum"
9518
+ ), isAssigneeUndefined && /* @__PURE__ */ React17.createElement(
9519
+ "li",
9520
+ {
9521
+ onClick: () => {
9522
+ setIsAssigneeDropdownOpen(false);
9523
+ setAssigneeSearchQuery("");
9524
+ },
9525
+ className: "px-3 py-2.5 text-sm cursor-pointer bg-red-500/10 text-red-300 flex items-center gap-2"
9526
+ },
9527
+ "Undefined (Removido)"
9528
+ ), viewMembers.filter((member) => {
9529
+ const search = assigneeSearchQuery.toLowerCase();
9530
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9531
+ }).map((member) => /* @__PURE__ */ React17.createElement(
9532
+ "li",
9533
+ {
9534
+ key: member.id,
9535
+ onClick: () => {
9536
+ setAssigneeId(member.id);
9537
+ setHasUnsavedChanges(true);
9538
+ setIsAssigneeDropdownOpen(false);
9539
+ setAssigneeSearchQuery("");
9540
+ },
9541
+ 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"}`
9542
+ },
9543
+ member.name || member.email || member.id
9544
+ )), viewMembers.filter((member) => {
9545
+ const search = assigneeSearchQuery.toLowerCase();
9546
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9547
+ }).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
9548
  "input",
9055
9549
  {
9056
9550
  type: "text",
@@ -9193,9 +9687,12 @@ function RelationshipDetailsPanel({
9193
9687
  const [description, setDescription] = useState20((link == null ? void 0 : link.description) ?? "");
9194
9688
  const [customProps, setCustomProps] = useState20(() => extractCustomPropsFromNode(link || {}));
9195
9689
  const [existingSections, setExistingSections] = useState20((link == null ? void 0 : link.description_sections) || []);
9690
+ const [sourceLabel, setSourceLabel] = useState20((link == null ? void 0 : link.source_label) ?? "");
9691
+ const [targetLabel, setTargetLabel] = useState20((link == null ? void 0 : link.target_label) ?? "");
9196
9692
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState20(false);
9197
9693
  const [isSaving, setIsSaving] = useState20(false);
9198
9694
  const [isReadMode, setIsReadMode] = useState20(false);
9695
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState20(false);
9199
9696
  const propsEndRef = useRef16(null);
9200
9697
  const canEdit = useMemo9(() => {
9201
9698
  const ability = defineAbilityFor(userRole);
@@ -9206,12 +9703,16 @@ function RelationshipDetailsPanel({
9206
9703
  setDescription((link == null ? void 0 : link.description) ?? "");
9207
9704
  setExistingSections((link == null ? void 0 : link.description_sections) || []);
9208
9705
  setCustomProps(extractCustomPropsFromNode(link || {}));
9706
+ setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
9707
+ setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
9708
+ setHasUnsavedChanges(false);
9209
9709
  }, [link]);
9210
9710
  const swallow = (e) => e.stopPropagation();
9211
9711
  const handleAddProp = () => {
9212
9712
  if (!canEdit) return;
9213
9713
  const newProp = createNewCustomProperty(customProps);
9214
9714
  setCustomProps((p) => [...p, newProp]);
9715
+ setHasUnsavedChanges(true);
9215
9716
  setTimeout(() => {
9216
9717
  var _a;
9217
9718
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -9223,6 +9724,12 @@ function RelationshipDetailsPanel({
9223
9724
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
9224
9725
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
9225
9726
  const currentName = overrides.name !== void 0 ? overrides.name : name;
9727
+ const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
9728
+ const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
9729
+ if (!keepOpen && !hasUnsavedChanges) {
9730
+ onClose();
9731
+ return;
9732
+ }
9226
9733
  setIsSaving(true);
9227
9734
  try {
9228
9735
  const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
@@ -9238,8 +9745,11 @@ function RelationshipDetailsPanel({
9238
9745
  isCurved: link.isCurved,
9239
9746
  curveOffset: link.curveOffset
9240
9747
  };
9748
+ if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
9749
+ if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
9241
9750
  await onSave(dataToSave, keepOpen);
9242
9751
  onDataUpdate(dataToSave);
9752
+ setHasUnsavedChanges(false);
9243
9753
  if (!keepOpen) {
9244
9754
  onClose();
9245
9755
  }
@@ -9253,18 +9763,21 @@ function RelationshipDetailsPanel({
9253
9763
  const handleSaveDescriptionInline = (newDescription) => {
9254
9764
  if (!canEdit) return;
9255
9765
  setDescription(newDescription);
9766
+ setHasUnsavedChanges(true);
9256
9767
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9257
9768
  triggerAutoSave({ description: newDescription });
9258
9769
  };
9259
9770
  const handleRemoveProp = (i) => {
9260
9771
  const newProps = customProps.filter((_, idx) => idx !== i);
9261
9772
  setCustomProps(newProps);
9773
+ setHasUnsavedChanges(true);
9262
9774
  triggerAutoSave({ customProps: newProps });
9263
9775
  };
9264
9776
  const handleUpdateProp = (index, updatedProp) => {
9265
9777
  const newProps = [...customProps];
9266
9778
  newProps[index] = updatedProp;
9267
9779
  setCustomProps(newProps);
9780
+ setHasUnsavedChanges(true);
9268
9781
  if (!updatedProp.isEditing) {
9269
9782
  triggerAutoSave({ customProps: newProps });
9270
9783
  }
@@ -9312,19 +9825,52 @@ function RelationshipDetailsPanel({
9312
9825
  onImageClick: handleImageClickFromText,
9313
9826
  onSaveDescription: handleSaveDescriptionInline
9314
9827
  }
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(
9828
+ ) : /* @__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
9829
  "input",
9317
9830
  {
9318
9831
  type: "text",
9319
9832
  value: name,
9320
- onChange: (e) => setName(e.target.value),
9833
+ onChange: (e) => {
9834
+ setName(e.target.value);
9835
+ setHasUnsavedChanges(true);
9836
+ },
9321
9837
  placeholder: "Ex: Controla, Pertence a, Fornece...",
9322
9838
  disabled: !canEdit,
9323
9839
  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
9840
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9325
9841
  `
9326
9842
  }
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(
9843
+ )), /* @__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(
9844
+ "input",
9845
+ {
9846
+ type: "text",
9847
+ value: sourceLabel,
9848
+ onChange: (e) => {
9849
+ setSourceLabel(e.target.value);
9850
+ setHasUnsavedChanges(true);
9851
+ },
9852
+ placeholder: "Ex: Conceitos",
9853
+ disabled: !canEdit,
9854
+ 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
9855
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9856
+ `
9857
+ }
9858
+ )), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ React19.createElement(
9859
+ "input",
9860
+ {
9861
+ type: "text",
9862
+ value: targetLabel,
9863
+ onChange: (e) => {
9864
+ setTargetLabel(e.target.value);
9865
+ setHasUnsavedChanges(true);
9866
+ },
9867
+ placeholder: "Ex: Refer\xEAncias",
9868
+ disabled: !canEdit,
9869
+ 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
9870
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9871
+ `
9872
+ }
9873
+ )))), /* @__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
9874
  DescriptionDisplay,
9329
9875
  {
9330
9876
  description,
@@ -9394,6 +9940,7 @@ function RelationshipDetailsPanel({
9394
9940
  onSave: (newDescription) => {
9395
9941
  if (!canEdit) return;
9396
9942
  setDescription(newDescription);
9943
+ setHasUnsavedChanges(true);
9397
9944
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9398
9945
  triggerAutoSave({ description: newDescription });
9399
9946
  },
@@ -9841,7 +10388,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9841
10388
  onMentionClick,
9842
10389
  onImageClick: handleImageClickFromText
9843
10390
  }
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(
10391
+ ) : /* @__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
10392
  "button",
9846
10393
  {
9847
10394
  onClick: () => setIsReadMode(true),
@@ -9875,7 +10422,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9875
10422
  // src/components/AncestryBoard.jsx
9876
10423
  import React24, { useState as useState24, useMemo as useMemo11, useEffect as useEffect21, useRef as useRef18 } from "react";
9877
10424
  import {
9878
- FiSearch as FiSearch4,
10425
+ FiSearch as FiSearch6,
9879
10426
  FiLayers as FiLayers6,
9880
10427
  FiCornerUpRight as FiCornerUpRight4,
9881
10428
  FiPlay,
@@ -10000,7 +10547,7 @@ var GroupItem = ({
10000
10547
  `,
10001
10548
  title: "Adicionar Ancestralidade a este grupo"
10002
10549
  },
10003
- isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch4, { size: 12 }),
10550
+ isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch6, { size: 12 }),
10004
10551
  isPickingForThisGroup ? "Selecionando..." : "Adicionar"
10005
10552
  ), /* @__PURE__ */ React24.createElement(
10006
10553
  "button",
@@ -10335,7 +10882,7 @@ function AncestryBoard({
10335
10882
  `
10336
10883
  },
10337
10884
  /* @__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,
10885
+ FiSearch6,
10339
10886
  {
10340
10887
  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
10888
  }
@@ -10493,7 +11040,8 @@ function XViewScene({
10493
11040
  save_ancestry_board_action,
10494
11041
  upload_file_action,
10495
11042
  delete_file_action,
10496
- check_user_permission
11043
+ check_user_permission,
11044
+ get_view_members
10497
11045
  }) {
10498
11046
  var _a, _b, _c, _d, _e, _f, _g, _h;
10499
11047
  const { data: session, status } = useSession();
@@ -10526,6 +11074,13 @@ function XViewScene({
10526
11074
  } else {
10527
11075
  setPermissionStatus("denied");
10528
11076
  setIsLoading(false);
11077
+ return;
11078
+ }
11079
+ if (get_view_members) {
11080
+ const membersRes = await get_view_members(owner_id, type, id);
11081
+ if (membersRes.success && membersRes.members) {
11082
+ setViewMembers(membersRes.members);
11083
+ }
10529
11084
  }
10530
11085
  } catch (error) {
10531
11086
  console.error("Erro ao verificar permiss\xE3o:", error);
@@ -10566,6 +11121,7 @@ function XViewScene({
10566
11121
  const [isLoading, setIsLoading] = useState25(true);
10567
11122
  const [permissionStatus, setPermissionStatus] = useState25("loading");
10568
11123
  const [userPermissionRole, setUserPermissionRole] = useState25(null);
11124
+ const [viewMembers, setViewMembers] = useState25([]);
10569
11125
  const [isInitialized, setIsInitialized] = useState25(false);
10570
11126
  const [sceneVersion, setSceneVersion] = useState25(0);
10571
11127
  const [contextMenu, setContextMenu] = useState25({
@@ -10634,6 +11190,7 @@ function XViewScene({
10634
11190
  });
10635
11191
  const [isImportModalOpen, setIsImportModalOpen] = useState25(false);
10636
11192
  const [importSuccessMessage, setImportSuccessMessage] = useState25("");
11193
+ const [invalidTargetError, setInvalidTargetError] = useState25(null);
10637
11194
  const [highlightedNodeId, setHighlightedNodeId] = useState25(null);
10638
11195
  const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState25(false);
10639
11196
  const [ancestryBoardData, setAncestryBoardData] = useState25([]);
@@ -12482,6 +13039,19 @@ function XViewScene({
12482
13039
  const handleStartVersioning = (nodeData) => {
12483
13040
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
12484
13041
  };
13042
+ const handleStartQuestQuick = useCallback4((questNode) => {
13043
+ var _a2;
13044
+ if (!questNode || !actionHandlerContext) return;
13045
+ const updatedNode = {
13046
+ ...questNode,
13047
+ status: "In Progress",
13048
+ color: "#eab308",
13049
+ assignee_id: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id
13050
+ };
13051
+ if (userActionHandlers.handleSaveNodeDetails) {
13052
+ userActionHandlers.handleSaveNodeDetails(actionHandlerContext, updatedNode);
13053
+ }
13054
+ }, [session, actionHandlerContext]);
12485
13055
  const handleCancelQuest = useCallback4(() => {
12486
13056
  const { graphGroup, ghostElements } = stateRef.current;
12487
13057
  if (ghostElements.node && graphGroup) {
@@ -14166,7 +14736,13 @@ function XViewScene({
14166
14736
  if (!parentDataRef.current) {
14167
14737
  return [];
14168
14738
  }
14169
- return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes).filter((node) => {
14739
+ return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
14740
+ const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
14741
+ return (fileData.nodes || []).map((node) => ({
14742
+ ...node,
14743
+ dataset_name: datasetName
14744
+ }));
14745
+ }).filter((node) => {
14170
14746
  var _a2;
14171
14747
  return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
14172
14748
  });
@@ -14310,6 +14886,9 @@ function XViewScene({
14310
14886
  }, 300);
14311
14887
  } else {
14312
14888
  setHasFocusedInitial(true);
14889
+ setInvalidTargetError(
14890
+ "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
14891
+ );
14313
14892
  }
14314
14893
  }
14315
14894
  }, [
@@ -14332,6 +14911,9 @@ function XViewScene({
14332
14911
  }, 300);
14333
14912
  } else {
14334
14913
  setHasOpenedInitialAncestry(true);
14914
+ setInvalidTargetError(
14915
+ "O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
14916
+ );
14335
14917
  }
14336
14918
  }
14337
14919
  }, [
@@ -14625,7 +15207,8 @@ function XViewScene({
14625
15207
  availableNodes: allAvailableNodes,
14626
15208
  availableAncestries: allAvailableAncestries,
14627
15209
  viewName: viewParams == null ? void 0 : viewParams.name,
14628
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1
15210
+ questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
15211
+ viewMembers
14629
15212
  }
14630
15213
  ),
14631
15214
  readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ React25.createElement(
@@ -14755,7 +15338,8 @@ function XViewScene({
14755
15338
  onMentionClick: handleAddExistingNode,
14756
15339
  onUploadFile: upload_file_action,
14757
15340
  userRole: userPermissionRole,
14758
- currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
15341
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
15342
+ viewMembers
14759
15343
  }
14760
15344
  ),
14761
15345
  detailsNode && !detailsNode.is_quest && /* @__PURE__ */ React25.createElement(
@@ -14868,7 +15452,10 @@ function XViewScene({
14868
15452
  onRenderAncestry: handleStartReadingAncestry,
14869
15453
  onEditAncestry: handleEditAncestry,
14870
15454
  onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
14871
- onFocusNode: handleFocusNode
15455
+ onFocusNode: handleFocusNode,
15456
+ viewMembers,
15457
+ currentUser: session == null ? void 0 : session.user,
15458
+ onStartQuest: handleStartQuestQuick
14872
15459
  }
14873
15460
  ),
14874
15461
  /* @__PURE__ */ React25.createElement(
@@ -14941,6 +15528,83 @@ function XViewScene({
14941
15528
  currentViewName: viewParams == null ? void 0 : viewParams.name,
14942
15529
  currentAncestries: ancestryDataRef.current || []
14943
15530
  }
15531
+ ),
15532
+ invalidTargetError && /* @__PURE__ */ React25.createElement(
15533
+ "div",
15534
+ {
15535
+ className: "ui-overlay",
15536
+ style: {
15537
+ position: "fixed",
15538
+ top: "24px",
15539
+ left: "50%",
15540
+ transform: "translateX(-50%)",
15541
+ zIndex: 1e4,
15542
+ padding: "16px 24px",
15543
+ background: "rgba(30, 20, 20, 0.85)",
15544
+ backdropFilter: "blur(12px)",
15545
+ WebkitBackdropFilter: "blur(12px)",
15546
+ border: "1px solid rgba(255, 70, 70, 0.35)",
15547
+ borderRadius: "16px",
15548
+ boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
15549
+ color: "#ffa0a0",
15550
+ display: "flex",
15551
+ alignItems: "center",
15552
+ gap: "16px",
15553
+ fontFamily: "Inter, sans-serif",
15554
+ animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
15555
+ }
15556
+ },
15557
+ /* @__PURE__ */ React25.createElement("style", null, `
15558
+ @keyframes fadeInDown {
15559
+ from { opacity: 0; transform: translate(-50%, -20px); }
15560
+ to { opacity: 1; transform: translate(-50%, 0); }
15561
+ }
15562
+ `),
15563
+ /* @__PURE__ */ React25.createElement(
15564
+ "svg",
15565
+ {
15566
+ width: "20",
15567
+ height: "20",
15568
+ viewBox: "0 0 24 24",
15569
+ fill: "none",
15570
+ stroke: "currentColor",
15571
+ strokeWidth: "2",
15572
+ strokeLinecap: "round",
15573
+ strokeLinejoin: "round"
15574
+ },
15575
+ /* @__PURE__ */ React25.createElement("circle", { cx: "12", cy: "12", r: "10" }),
15576
+ /* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
15577
+ /* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
15578
+ ),
15579
+ /* @__PURE__ */ React25.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
15580
+ /* @__PURE__ */ React25.createElement(
15581
+ "button",
15582
+ {
15583
+ onClick: () => setInvalidTargetError(null),
15584
+ style: {
15585
+ background: "rgba(255, 255, 255, 0.1)",
15586
+ border: "none",
15587
+ color: "white",
15588
+ padding: "4px 10px",
15589
+ borderRadius: "8px",
15590
+ cursor: "pointer",
15591
+ fontSize: "12px",
15592
+ fontWeight: 600,
15593
+ transition: "all 0.2s",
15594
+ marginLeft: "8px",
15595
+ border: "1px solid rgba(255,255,255,0.1)"
15596
+ },
15597
+ onMouseOver: (e) => {
15598
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
15599
+ e.currentTarget.style.transform = "translateY(-1px)";
15600
+ },
15601
+ onMouseOut: (e) => {
15602
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
15603
+ e.currentTarget.style.transform = "translateY(0)";
15604
+ }
15605
+ },
15606
+ "Fechar"
15607
+ )
14944
15608
  )
14945
15609
  );
14946
15610
  }
@@ -15367,6 +16031,17 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
15367
16031
  return { success: false, error: error.message };
15368
16032
  }
15369
16033
  }
16034
+ async function get_view_members_logic(db_services, ownerId, type, itemId) {
16035
+ try {
16036
+ if (!db_services.get_view_members) {
16037
+ return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
16038
+ }
16039
+ return await db_services.get_view_members(ownerId, type, itemId);
16040
+ } catch (error) {
16041
+ console.error("Erro em get_view_members_logic:", error);
16042
+ return { success: false, error: error.message };
16043
+ }
16044
+ }
15370
16045
  export {
15371
16046
  XViewScene,
15372
16047
  delete_uploaded_file_logic,
@@ -15374,6 +16049,7 @@ export {
15374
16049
  get_ancestry_file_logic,
15375
16050
  get_scene_view_data_logic,
15376
16051
  get_single_parent_file_logic,
16052
+ get_view_members_logic,
15377
16053
  import_parent_file_modal_get_logic,
15378
16054
  save_ancestry_board_logic,
15379
16055
  save_view_data_logic,