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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +850 -156
  2. package/dist/index.mjs +854 -161
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  get_ancestry_file_logic: () => get_ancestry_file_logic,
36
36
  get_scene_view_data_logic: () => get_scene_view_data_logic,
37
37
  get_single_parent_file_logic: () => get_single_parent_file_logic,
38
+ get_view_members_logic: () => get_view_members_logic,
38
39
  import_parent_file_modal_get_logic: () => import_parent_file_modal_get_logic,
39
40
  save_ancestry_board_logic: () => save_ancestry_board_logic,
40
41
  save_view_data_logic: () => save_view_data_logic,
@@ -141,7 +142,10 @@ function ContextMenu({
141
142
  onEditAncestry,
142
143
  onDeleteAncestry,
143
144
  onFocusNode,
144
- onClose
145
+ onClose,
146
+ viewMembers,
147
+ currentUser,
148
+ onStartQuest
145
149
  }) {
146
150
  var _a, _b, _c;
147
151
  const menuRef = (0, import_react.useRef)(null);
@@ -149,6 +153,7 @@ function ContextMenu({
149
153
  const [menuView, setMenuView] = (0, import_react.useState)("main");
150
154
  const [selectedAncestry, setSelectedAncestry] = (0, import_react.useState)(null);
151
155
  const [versionSubMenu, setVersionSubMenu] = (0, import_react.useState)(null);
156
+ const [labelSubMenu, setLabelSubMenu] = (0, import_react.useState)(null);
152
157
  const [isLinkCopied, setIsLinkCopied] = (0, import_react.useState)(false);
153
158
  const [selectedQuestStatus, setSelectedQuestStatus] = (0, import_react.useState)(null);
154
159
  const ability = (0, import_react.useMemo)(() => defineAbilityFor(userRole), [userRole]);
@@ -157,6 +162,7 @@ function ContextMenu({
157
162
  setMenuView("main");
158
163
  setSelectedAncestry(null);
159
164
  setVersionSubMenu(null);
165
+ setLabelSubMenu(null);
160
166
  setSelectedQuestStatus(null);
161
167
  }
162
168
  }, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
@@ -172,7 +178,7 @@ function ContextMenu({
172
178
  if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
173
179
  if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
174
180
  setMenuPos({ left, top });
175
- }, [data, menuView, versionSubMenu, selectedQuestStatus]);
181
+ }, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
176
182
  (0, import_react.useEffect)(() => {
177
183
  if (!data.visible) return;
178
184
  const handleClickOutside = (e) => {
@@ -224,7 +230,21 @@ function ContextMenu({
224
230
  var _a2;
225
231
  return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
226
232
  });
227
- const groupedConnections = commonConnections.reduce((acc, conn) => {
233
+ const getLabelForConnection = (conn) => {
234
+ if (conn.direction === "outgoing") return conn.link.source_label || null;
235
+ if (conn.direction === "incoming") return conn.link.target_label || null;
236
+ return null;
237
+ };
238
+ const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
239
+ const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
240
+ const labelGroups = commonWithLabel.reduce((acc, conn) => {
241
+ const label = getLabelForConnection(conn);
242
+ if (!acc[label]) acc[label] = [];
243
+ acc[label].push(conn);
244
+ return acc;
245
+ }, {});
246
+ const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
247
+ const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
228
248
  var _a2;
229
249
  const { targetNode } = conn;
230
250
  const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
@@ -326,12 +346,25 @@ function ContextMenu({
326
346
  });
327
347
  };
328
348
  const renderMainView = () => {
329
- var _a2;
349
+ var _a2, _b2, _c2;
330
350
  const hasVersions = computedVersions.length > 0;
331
351
  const canCreateVersion = ability.can("create", "Versioning");
332
352
  const canReadVersion = ability.can("read", "Versioning");
333
353
  const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
334
- return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_a2 = data.nodeData) == null ? void 0 : _a2.is_quest) && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
354
+ 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));
355
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, canStartQuest && /* @__PURE__ */ import_react.default.createElement(
356
+ "button",
357
+ {
358
+ onClick: () => {
359
+ onStartQuest == null ? void 0 : onStartQuest(data.nodeData);
360
+ onClose();
361
+ },
362
+ 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`,
363
+ title: "Atribuir a mim e iniciar"
364
+ },
365
+ /* @__PURE__ */ import_react.default.createElement("span", null, "\u{1F680}"),
366
+ /* @__PURE__ */ import_react.default.createElement("span", null, "Iniciar Quest")
367
+ ), ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_c2 = data.nodeData) == null ? void 0 : _c2.is_quest) && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
335
368
  onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
336
369
  onClose();
337
370
  }, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.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__ */ import_react.default.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__ */ import_react.default.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Excluir Node"))));
@@ -352,11 +385,32 @@ function ContextMenu({
352
385
  /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
353
386
  ))));
354
387
  };
388
+ const renderLabelSubMenuView = () => {
389
+ const group = labelSubMenu;
390
+ const isScrollable = group.connections.length > 10;
391
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
392
+ setLabelSubMenu(null);
393
+ setMenuView("connections");
394
+ }, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ import_react.default.createElement(
395
+ "button",
396
+ {
397
+ key: conn.targetNode.id,
398
+ onClick: () => handleExpandAndClose([conn.link]),
399
+ className: baseButtonClass,
400
+ title: `Expandir conex\xE3o com ${conn.targetNode.name}`
401
+ },
402
+ /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ import_react.default.createElement("polyline", { points: "12 5 19 12 12 19" })),
403
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
404
+ ))));
405
+ };
355
406
  const renderConnectionsView = () => {
356
407
  if (versionSubMenu) {
357
408
  return renderVersionSubMenuView();
358
409
  }
359
- const totalItems = availableAncestries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
410
+ if (labelSubMenu) {
411
+ return renderLabelSubMenuView();
412
+ }
413
+ const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
360
414
  const isScrollable = totalItems > 10;
361
415
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ import_react.default.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ import_react.default.createElement(
362
416
  "button",
@@ -368,7 +422,20 @@ function ContextMenu({
368
422
  },
369
423
  /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "21", r: "2" })),
370
424
  /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
371
- ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
425
+ ))), labelGroupEntries.length > 0 && /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ import_react.default.createElement(
426
+ "button",
427
+ {
428
+ key: label,
429
+ onClick: () => {
430
+ setLabelSubMenu({ label, connections: conns });
431
+ },
432
+ className: baseButtonClass,
433
+ title: `Ver grupo: ${label}`
434
+ },
435
+ /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400" }, /* @__PURE__ */ import_react.default.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__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
436
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, label),
437
+ /* @__PURE__ */ import_react.default.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
438
+ ))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
372
439
  if (group.isVersionGroup) {
373
440
  return /* @__PURE__ */ import_react.default.createElement(
374
441
  "button",
@@ -552,15 +619,18 @@ function XViewSidebar({
552
619
  const [isFilterMenuOpen, setIsFilterMenuOpen] = (0, import_react2.useState)(false);
553
620
  const [filterCategory, setFilterCategory] = (0, import_react2.useState)("Type");
554
621
  const [filterValue, setFilterValue] = (0, import_react2.useState)("");
622
+ const [isTypeDropdownOpen, setIsTypeDropdownOpen] = (0, import_react2.useState)(false);
623
+ const [highlightedTypeIndex, setHighlightedTypeIndex] = (0, import_react2.useState)(-1);
555
624
  const containerRef = (0, import_react2.useRef)(null);
556
625
  const inputRef = (0, import_react2.useRef)(null);
557
626
  const filterMenuRef = (0, import_react2.useRef)(null);
627
+ const typeDropdownRef = (0, import_react2.useRef)(null);
558
628
  const ability = (0, import_react2.useMemo)(() => {
559
629
  return defineAbilityFor(userRole);
560
630
  }, [userRole]);
561
631
  const contextLabel = (0, import_react2.useMemo)(() => {
562
632
  if (!viewType || !viewName) return null;
563
- const typeLower = viewType.toLowerCase();
633
+ const typeLower = String(viewType).toLowerCase();
564
634
  if (typeLower === "database") {
565
635
  return `Dataset: ${viewName}`;
566
636
  } else if (typeLower === "view") {
@@ -570,7 +640,12 @@ function XViewSidebar({
570
640
  }
571
641
  return `${viewType}: ${viewName}`;
572
642
  }, [viewType, viewName]);
573
- const normalize = (str = "") => String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
643
+ const normalize = (str = "") => {
644
+ if (str === void 0 || str === null) {
645
+ console.warn("XViewSidebar: normalize recebeu valor nulo/indefinido:", str);
646
+ }
647
+ return String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
648
+ };
574
649
  const collator = (0, import_react2.useMemo)(
575
650
  () => new Intl.Collator("pt-BR", { sensitivity: "base", numeric: true }),
576
651
  []
@@ -579,13 +654,30 @@ function XViewSidebar({
579
654
  const typesSet = /* @__PURE__ */ new Set();
580
655
  (dbNodes || []).forEach((node) => {
581
656
  if (Array.isArray(node.type)) {
582
- node.type.forEach((t) => typesSet.add(t));
583
- } else if (node.type) {
657
+ node.type.forEach((t) => {
658
+ if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
659
+ });
660
+ } else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
584
661
  typesSet.add(node.type);
585
662
  }
586
663
  });
587
664
  return Array.from(typesSet).sort();
588
665
  }, [dbNodes]);
666
+ const availableDatasets = (0, import_react2.useMemo)(() => {
667
+ const datasetsSet = /* @__PURE__ */ new Set();
668
+ (dbNodes || []).forEach((node) => {
669
+ if (node.dataset_name) {
670
+ datasetsSet.add(node.dataset_name);
671
+ }
672
+ });
673
+ return Array.from(datasetsSet).sort();
674
+ }, [dbNodes]);
675
+ const filteredAutocompleteOptions = (0, import_react2.useMemo)(() => {
676
+ const source = filterCategory === "Dataset" ? availableDatasets : availableTypes;
677
+ if (!filterValue.trim()) return source;
678
+ const search = filterValue.toLowerCase().trim();
679
+ return source.filter((item) => item.toLowerCase().includes(search));
680
+ }, [availableTypes, availableDatasets, filterValue, filterCategory]);
589
681
  const filteredAndSorted = (0, import_react2.useMemo)(() => {
590
682
  const base = Array.isArray(dbNodes) ? dbNodes : [];
591
683
  let pool = base;
@@ -605,6 +697,8 @@ function XViewSidebar({
605
697
  return nodeTypes.some((t) => String(t).toLowerCase() === String(filter.value).toLowerCase());
606
698
  } else if (filter.type === "Color") {
607
699
  return String(node.color).toLowerCase() === String(filter.value).toLowerCase();
700
+ } else if (filter.type === "Dataset") {
701
+ return String(node.dataset_name || "").toLowerCase() === String(filter.value).toLowerCase();
608
702
  }
609
703
  return true;
610
704
  });
@@ -620,18 +714,21 @@ function XViewSidebar({
620
714
  inView.sort(byName);
621
715
  const ordered = [...notInView, ...inView];
622
716
  return ordered.slice(0, 200);
623
- }, [dbNodes, query, isNodeInView, viewVersion, activeFilters]);
717
+ }, [dbNodes, query, isNodeInView, viewVersion, activeFilters, filterCategory]);
624
718
  (0, import_react2.useEffect)(() => {
625
719
  const handleClickOutside = (event) => {
626
720
  if (filterMenuRef.current && !filterMenuRef.current.contains(event.target)) {
627
721
  setIsFilterMenuOpen(false);
628
722
  }
723
+ if (typeDropdownRef.current && !typeDropdownRef.current.contains(event.target)) {
724
+ setIsTypeDropdownOpen(false);
725
+ }
629
726
  };
630
- if (isFilterMenuOpen) {
727
+ if (isFilterMenuOpen || isTypeDropdownOpen) {
631
728
  document.addEventListener("mousedown", handleClickOutside);
632
729
  }
633
730
  return () => document.removeEventListener("mousedown", handleClickOutside);
634
- }, [isFilterMenuOpen]);
731
+ }, [isFilterMenuOpen, isTypeDropdownOpen]);
635
732
  (0, import_react2.useEffect)(() => {
636
733
  if (!query) setSelectedNodeId(null);
637
734
  }, [query]);
@@ -685,6 +782,35 @@ function XViewSidebar({
685
782
  const handleRemoveFilter = (index) => {
686
783
  setActiveFilters((prev) => prev.filter((_, i) => i !== index));
687
784
  };
785
+ const handleTypeKeyDown = (e) => {
786
+ if (!isTypeDropdownOpen) {
787
+ setIsTypeDropdownOpen(true);
788
+ }
789
+ if (e.key === "ArrowDown") {
790
+ e.preventDefault();
791
+ setHighlightedTypeIndex(
792
+ (prev) => prev < filteredAutocompleteOptions.length - 1 ? prev + 1 : prev
793
+ );
794
+ } else if (e.key === "ArrowUp") {
795
+ e.preventDefault();
796
+ setHighlightedTypeIndex((prev) => prev > 0 ? prev - 1 : 0);
797
+ } else if (e.key === "Enter") {
798
+ e.preventDefault();
799
+ if (highlightedTypeIndex >= 0 && filteredAutocompleteOptions[highlightedTypeIndex]) {
800
+ setFilterValue(filteredAutocompleteOptions[highlightedTypeIndex]);
801
+ setIsTypeDropdownOpen(false);
802
+ setHighlightedTypeIndex(-1);
803
+ } else {
804
+ handleAddFilter();
805
+ }
806
+ } else if (e.key === "Escape") {
807
+ e.preventDefault();
808
+ setIsTypeDropdownOpen(false);
809
+ setHighlightedTypeIndex(-1);
810
+ } else if (e.key === "Tab") {
811
+ setIsTypeDropdownOpen(false);
812
+ }
813
+ };
688
814
  const ToggleButton = /* @__PURE__ */ import_react2.default.createElement(
689
815
  "button",
690
816
  {
@@ -704,7 +830,7 @@ function XViewSidebar({
704
830
  "div",
705
831
  {
706
832
  ref: containerRef,
707
- className: "ui-overlay fixed left-0 top-0 h-full w-[min(92vw,320px)] z-40",
833
+ className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
708
834
  onPointerDown: swallow,
709
835
  onPointerMove: swallow,
710
836
  onPointerUp: swallow,
@@ -713,7 +839,7 @@ function XViewSidebar({
713
839
  onContextMenu: swallow,
714
840
  onDoubleClick: swallow
715
841
  },
716
- /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
842
+ /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
717
843
  "button",
718
844
  {
719
845
  className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
@@ -770,7 +896,7 @@ function XViewSidebar({
770
896
  autoComplete: "off"
771
897
  }
772
898
  )
773
- ), showList && /* @__PURE__ */ import_react2.default.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) => {
899
+ ), showList && /* @__PURE__ */ import_react2.default.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) => {
774
900
  const inView = isNodeInView(n.id);
775
901
  const active = selectedNodeId === n.id;
776
902
  const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
@@ -804,7 +930,7 @@ function XViewSidebar({
804
930
  },
805
931
  /* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })),
806
932
  "Filters"
807
- ), isFilterMenuOpen && /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color"].map((opt) => /* @__PURE__ */ import_react2.default.createElement(
933
+ ), isFilterMenuOpen && /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ import_react2.default.createElement(
808
934
  "button",
809
935
  {
810
936
  key: opt,
@@ -815,18 +941,36 @@ function XViewSidebar({
815
941
  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"}`
816
942
  },
817
943
  opt
818
- )))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react2.default.createElement(
944
+ )))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" || filterCategory === "Dataset" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ import_react2.default.createElement(
819
945
  "input",
820
946
  {
821
- list: "typeOptions",
822
947
  type: "text",
823
948
  value: filterValue,
824
- onChange: (e) => setFilterValue(e.target.value),
825
- placeholder: "Ex: Concept",
949
+ onChange: (e) => {
950
+ setFilterValue(e.target.value);
951
+ if (!isTypeDropdownOpen) setIsTypeDropdownOpen(true);
952
+ setHighlightedTypeIndex(-1);
953
+ },
954
+ onFocus: () => setIsTypeDropdownOpen(true),
955
+ onKeyDown: handleTypeKeyDown,
956
+ placeholder: filterCategory === "Type" ? "Ex: Concept" : "Ex: Dataset 1",
826
957
  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",
827
958
  autoFocus: true
828
959
  }
829
- ), /* @__PURE__ */ import_react2.default.createElement("datalist", { id: "typeOptions" }, availableTypes.map((t) => /* @__PURE__ */ import_react2.default.createElement("option", { key: t, value: t })))) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ import_react2.default.createElement(
960
+ ), isTypeDropdownOpen && filteredAutocompleteOptions.length > 0 && /* @__PURE__ */ import_react2.default.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__ */ import_react2.default.createElement(
961
+ "div",
962
+ {
963
+ key: t,
964
+ 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"}`,
965
+ onMouseDown: (e) => {
966
+ e.preventDefault();
967
+ setFilterValue(t);
968
+ setIsTypeDropdownOpen(false);
969
+ setHighlightedTypeIndex(-1);
970
+ }
971
+ },
972
+ t
973
+ )))) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ import_react2.default.createElement(
830
974
  "div",
831
975
  {
832
976
  className: "absolute left-2 w-3 h-3 rounded-full border border-white/20 shadow-sm",
@@ -2625,7 +2769,15 @@ var userActionHandlers = {
2625
2769
  var _a;
2626
2770
  const { graphDataRef, sceneDataRef, stateRef, setters } = context;
2627
2771
  if (!graphDataRef.current || !sceneDataRef.current) return;
2628
- const { _baseEmissiveIntensity: ignored, ...nodeToSave } = updatedNode;
2772
+ const {
2773
+ _baseEmissiveIntensity,
2774
+ _baseScale,
2775
+ labelObject,
2776
+ labelOffset,
2777
+ timelineIntervalBar,
2778
+ timelineEndLabel,
2779
+ ...nodeToSave
2780
+ } = updatedNode;
2629
2781
  const parentInfo = getParentFileInfoForNode(graphDataRef.current, sceneDataRef.current, nodeToSave.id, context.sceneConfigId, context.ownerId);
2630
2782
  if (!parentInfo || !parentInfo.ownerId) {
2631
2783
  console.error("N\xE3o foi poss\xEDvel encontrar as informa\xE7\xF5es do arquivo pai (ou ownerId) para o Node a ser atualizado:", nodeToSave.id);
@@ -2814,18 +2966,27 @@ function createNewCustomProperty(existingProps = []) {
2814
2966
  };
2815
2967
  }
2816
2968
  var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
2817
- const match = refString.match(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/);
2969
+ const match = refString.match(
2970
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
2971
+ );
2818
2972
  if (!match) return null;
2819
2973
  const [_, type, itemId, sectionId] = match;
2820
2974
  let sourceItem = null;
2821
2975
  if (type === "node") {
2822
2976
  sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
2823
2977
  } else {
2824
- sourceItem = availableAncestries.find((a) => String(a.ancestry_id) === String(itemId));
2978
+ sourceItem = availableAncestries.find(
2979
+ (a) => String(a.ancestry_id) === String(itemId)
2980
+ );
2825
2981
  }
2826
2982
  if (!sourceItem) return null;
2827
- const sections = parseDescriptionSections(sourceItem.description, sourceItem.description_sections);
2828
- const targetSection = sections.find((s) => String(s.id) === String(sectionId));
2983
+ const sections = parseDescriptionSections(
2984
+ sourceItem.description,
2985
+ sourceItem.description_sections
2986
+ );
2987
+ const targetSection = sections.find(
2988
+ (s) => String(s.id) === String(sectionId)
2989
+ );
2829
2990
  if (!targetSection) return null;
2830
2991
  return {
2831
2992
  content: targetSection.content,
@@ -2839,21 +3000,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2839
3000
  let text = rawText;
2840
3001
  const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
2841
3002
  const allAncestries = ancestryData || [];
2842
- text = text.replace(/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g, (match, type, itemId, sectionId) => {
2843
- const resolved = resolveDescriptionReference(match, allNodes, allAncestries);
2844
- if (resolved && !resolved.error) {
2845
- return resolved.content;
3003
+ text = text.replace(
3004
+ /\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
3005
+ (match, type, itemId, sectionId) => {
3006
+ const resolved = resolveDescriptionReference(
3007
+ match,
3008
+ allNodes,
3009
+ allAncestries
3010
+ );
3011
+ if (resolved && !resolved.error) {
3012
+ return resolved.content;
3013
+ }
3014
+ return "[Refer\xEAncia indispon\xEDvel]";
2846
3015
  }
2847
- return "[Refer\xEAncia indispon\xEDvel]";
2848
- });
3016
+ );
2849
3017
  text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
2850
- text = text.replace(/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g, (match, nodeId) => {
2851
- const node = allNodes.find((n) => String(n.id) === String(nodeId));
2852
- return node ? `@${node.name}` : `@Men\xE7\xE3o`;
2853
- });
2854
- text = text.replace(/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g, (match, url, name) => {
2855
- return name ? `[Imagem: ${name}]` : `[Imagem]`;
2856
- });
3018
+ text = text.replace(
3019
+ /\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
3020
+ (match, nodeId) => {
3021
+ const node = allNodes.find((n) => String(n.id) === String(nodeId));
3022
+ return node ? `@${node.name}` : `@Men\xE7\xE3o`;
3023
+ }
3024
+ );
3025
+ text = text.replace(
3026
+ /\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
3027
+ (match, url, name) => {
3028
+ return name ? `[Imagem: ${name}]` : `[Imagem]`;
3029
+ }
3030
+ );
2857
3031
  text = text.replace(/^#+\s*/gm, "");
2858
3032
  text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
2859
3033
  text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
@@ -2861,7 +3035,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
2861
3035
  return text.trim();
2862
3036
  }
2863
3037
  function generateTooltipHtml(data, parentData, ancestryData) {
2864
- const ignoredKeys = ["id", "name", "type", "color", "_baseEmissiveIntensity", "description"];
3038
+ const ignoredKeys = [
3039
+ "id",
3040
+ "name",
3041
+ "type",
3042
+ "color",
3043
+ "_baseEmissiveIntensity",
3044
+ "description"
3045
+ ];
2865
3046
  const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
2866
3047
  const extras = customKeys.length;
2867
3048
  let typeDisplay = "Node";
@@ -2876,7 +3057,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
2876
3057
  <div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
2877
3058
  <div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
2878
3059
  if (data.description) {
2879
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3060
+ const cleanDesc = formatDescriptionForTooltip(
3061
+ data.description,
3062
+ parentData,
3063
+ ancestryData
3064
+ );
2880
3065
  if (cleanDesc) {
2881
3066
  html += `<div style="
2882
3067
  margin-top: 6px;
@@ -2914,7 +3099,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
2914
3099
  html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
2915
3100
  }
2916
3101
  if (hasDescription) {
2917
- const cleanDesc = formatDescriptionForTooltip(data.description, parentData, ancestryData);
3102
+ const cleanDesc = formatDescriptionForTooltip(
3103
+ data.description,
3104
+ parentData,
3105
+ ancestryData
3106
+ );
2918
3107
  if (cleanDesc) {
2919
3108
  html += `<div style="
2920
3109
  display: -webkit-box;
@@ -2996,7 +3185,8 @@ var IGNORED_KEYS = [
2996
3185
  "isAddingAbstractionNodes",
2997
3186
  "status",
2998
3187
  "is_quest",
2999
- "raw_title"
3188
+ "raw_title",
3189
+ "assignee_id"
3000
3190
  ];
3001
3191
  function extractCustomPropsFromNode(node) {
3002
3192
  const customPropTypes = node._customPropTypes || {};
@@ -3006,16 +3196,40 @@ function extractCustomPropsFromNode(node) {
3006
3196
  if (t === "date") {
3007
3197
  if (val && typeof val === "object") {
3008
3198
  if ("date_interval" in val) {
3009
- return { id: v4_default(), key, type: "date", value: { type: "Date Interval", start: val.date_interval.start || "", end: val.date_interval.end || "" } };
3199
+ return {
3200
+ id: v4_default(),
3201
+ key,
3202
+ type: "date",
3203
+ value: {
3204
+ type: "Date Interval",
3205
+ start: val.date_interval.start || "",
3206
+ end: val.date_interval.end || ""
3207
+ }
3208
+ };
3010
3209
  }
3011
3210
  if ("year" in val) {
3012
- return { id: v4_default(), key, type: "date", value: { type: "Ano", value: val.year || "" } };
3211
+ return {
3212
+ id: v4_default(),
3213
+ key,
3214
+ type: "date",
3215
+ value: { type: "Ano", value: val.year || "" }
3216
+ };
3013
3217
  }
3014
3218
  if ("date" in val) {
3015
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: val.date || "" } };
3219
+ return {
3220
+ id: v4_default(),
3221
+ key,
3222
+ type: "date",
3223
+ value: { type: "Data", value: val.date || "" }
3224
+ };
3016
3225
  }
3017
3226
  }
3018
- return { id: v4_default(), key, type: "date", value: { type: "Data", value: "" } };
3227
+ return {
3228
+ id: v4_default(),
3229
+ key,
3230
+ type: "date",
3231
+ value: { type: "Data", value: "" }
3232
+ };
3019
3233
  }
3020
3234
  if (t === "list" || t === "links" || t === "images" || t === "documents") {
3021
3235
  const safeList = (val || []).map((item) => ({
@@ -3025,7 +3239,12 @@ function extractCustomPropsFromNode(node) {
3025
3239
  }));
3026
3240
  return { id: v4_default(), key, type: t, value: safeList };
3027
3241
  }
3028
- return { id: v4_default(), key, type: t, value: t === "number" ? Number(val) || 0 : String(val ?? "") };
3242
+ return {
3243
+ id: v4_default(),
3244
+ key,
3245
+ type: t,
3246
+ value: t === "number" ? Number(val) || 0 : String(val ?? "")
3247
+ };
3029
3248
  });
3030
3249
  }
3031
3250
  function toObjectFromCustomProps(customProps) {
@@ -3040,7 +3259,12 @@ function toObjectFromCustomProps(customProps) {
3040
3259
  const { type: uiDateType, ...dateValues } = p.value || {};
3041
3260
  if (uiDateType) {
3042
3261
  if (uiDateType === "Date Interval") {
3043
- out[key] = { date_interval: { start: dateValues.start || "", end: dateValues.end || "" } };
3262
+ out[key] = {
3263
+ date_interval: {
3264
+ start: dateValues.start || "",
3265
+ end: dateValues.end || ""
3266
+ }
3267
+ };
3044
3268
  } else if (uiDateType === "Ano") {
3045
3269
  out[key] = { year: dateValues.value || "" };
3046
3270
  } else {
@@ -3076,10 +3300,20 @@ function createTextSprite(text, fontSize = 64) {
3076
3300
  context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
3077
3301
  const texture = new THREE2.CanvasTexture(canvas);
3078
3302
  texture.colorSpace = THREE2.SRGBColorSpace;
3079
- const spriteMaterial = new THREE2.SpriteMaterial({ map: texture, sizeAttenuation: true, depthWrite: false, transparent: true, toneMapped: false });
3303
+ const spriteMaterial = new THREE2.SpriteMaterial({
3304
+ map: texture,
3305
+ sizeAttenuation: true,
3306
+ depthWrite: false,
3307
+ transparent: true,
3308
+ toneMapped: false
3309
+ });
3080
3310
  const sprite = new THREE2.Sprite(spriteMaterial);
3081
3311
  const scaleFactor = 0.05;
3082
- sprite.scale.set(canvas.width * scaleFactor, canvas.height * scaleFactor, 1);
3312
+ sprite.scale.set(
3313
+ canvas.width * scaleFactor,
3314
+ canvas.height * scaleFactor,
3315
+ 1
3316
+ );
3083
3317
  sprite.layers.set(DEFAULT_LAYER);
3084
3318
  return sprite;
3085
3319
  }
@@ -3095,7 +3329,14 @@ function makeGlowTexture() {
3095
3329
  const canvas = document.createElement("canvas");
3096
3330
  canvas.width = canvas.height = size;
3097
3331
  const ctx = canvas.getContext("2d");
3098
- const grd = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
3332
+ const grd = ctx.createRadialGradient(
3333
+ size / 2,
3334
+ size / 2,
3335
+ 0,
3336
+ size / 2,
3337
+ size / 2,
3338
+ size / 2
3339
+ );
3099
3340
  grd.addColorStop(0, "rgba(255,255,255,1)");
3100
3341
  grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
3101
3342
  grd.addColorStop(1, "rgba(255,255,255,0)");
@@ -3111,8 +3352,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
3111
3352
  let midBoost = 1 - Math.abs(L - 0.5) * 2;
3112
3353
  midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
3113
3354
  const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
3114
- const auraColor = new THREE2.Color(hexColor).lerp(new THREE2.Color("#ffffff"), 0.25);
3115
- const auraMat = new THREE2.SpriteMaterial({ map: glowTexture, color: auraColor, opacity: auraOpacity, blending: THREE2.AdditiveBlending, transparent: true, depthWrite: false, toneMapped: false });
3355
+ const auraColor = new THREE2.Color(hexColor).lerp(
3356
+ new THREE2.Color("#ffffff"),
3357
+ 0.25
3358
+ );
3359
+ const auraMat = new THREE2.SpriteMaterial({
3360
+ map: glowTexture,
3361
+ color: auraColor,
3362
+ opacity: auraOpacity,
3363
+ blending: THREE2.AdditiveBlending,
3364
+ transparent: true,
3365
+ depthWrite: false,
3366
+ toneMapped: false
3367
+ });
3116
3368
  const aura = new THREE2.Sprite(auraMat);
3117
3369
  aura.scale.setScalar(auraScale);
3118
3370
  aura.name = "aura";
@@ -3145,7 +3397,17 @@ function calculateNodePositions(nodes) {
3145
3397
  }
3146
3398
  return positions;
3147
3399
  }
3148
- function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
3400
+ function updateTooltip({
3401
+ tooltipEl,
3402
+ hoveredNode,
3403
+ hoveredLink,
3404
+ camera,
3405
+ mountEl,
3406
+ isSceneBusy,
3407
+ parentData,
3408
+ ancestryData
3409
+ }) {
3410
+ var _a, _b;
3149
3411
  if (!tooltipEl || !camera || !mountEl) return;
3150
3412
  let content = "";
3151
3413
  let positionTarget = null;
@@ -3155,20 +3417,35 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3155
3417
  currentId = `node_${hoveredNode.userData.id}`;
3156
3418
  positionTarget = hoveredNode;
3157
3419
  if (tooltipEl.dataset.currentId !== currentId) {
3158
- content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
3420
+ content = generateTooltipHtml(
3421
+ hoveredNode.userData,
3422
+ parentData,
3423
+ ancestryData
3424
+ );
3159
3425
  }
3160
3426
  } else if (hoveredLink && !isSceneBusy) {
3161
- currentId = `link_${hoveredLink.userData.id}`;
3162
- if (hoveredLink.userData.isCurved) {
3163
- const positions = hoveredLink.geometry.attributes.position.array;
3164
- const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3165
- positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
3166
- } else {
3167
- positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
3168
- }
3169
- isLink = true;
3170
- if (tooltipEl.dataset.currentId !== currentId) {
3171
- content = hoveredLink.userData.isAncestryLink ? generateLinkTooltipHtml(hoveredLink.userData.relationship || {}, parentData, ancestryData) : generateLinkTooltipHtml(hoveredLink.userData, parentData, ancestryData);
3427
+ const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
3428
+ const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
3429
+ if (hasContent) {
3430
+ currentId = `link_${hoveredLink.userData.id}`;
3431
+ if (hoveredLink.userData.isCurved) {
3432
+ const positions = hoveredLink.geometry.attributes.position.array;
3433
+ const midIndex = Math.floor(positions.length / 2 / 3) * 3;
3434
+ positionTarget = new THREE2.Vector3(
3435
+ positions[midIndex],
3436
+ positions[midIndex + 1],
3437
+ positions[midIndex + 2]
3438
+ );
3439
+ } else {
3440
+ positionTarget = new THREE2.Vector3().addVectors(
3441
+ hoveredLink.userData.sourceNode.position,
3442
+ hoveredLink.userData.targetNode.position
3443
+ ).multiplyScalar(0.5);
3444
+ }
3445
+ isLink = true;
3446
+ if (tooltipEl.dataset.currentId !== currentId) {
3447
+ content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
3448
+ }
3172
3449
  }
3173
3450
  }
3174
3451
  if (positionTarget) {
@@ -3192,9 +3469,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
3192
3469
  const tooltipRect = tooltipEl.getBoundingClientRect();
3193
3470
  const offsetX = 20;
3194
3471
  const offsetY = 20;
3195
- if (x + tooltipRect.width + offsetX > clientWidth) x = x - tooltipRect.width - offsetX;
3472
+ if (x + tooltipRect.width + offsetX > clientWidth)
3473
+ x = x - tooltipRect.width - offsetX;
3196
3474
  else x = x + offsetX;
3197
- if (y + tooltipRect.height + offsetY > clientHeight) y = y - tooltipRect.height - offsetY;
3475
+ if (y + tooltipRect.height + offsetY > clientHeight)
3476
+ y = y - tooltipRect.height - offsetY;
3198
3477
  else y = y + offsetY;
3199
3478
  tooltipEl.style.display = "block";
3200
3479
  tooltipEl.style.left = `${x}px`;
@@ -3228,7 +3507,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
3228
3507
  const content = parts[i + 2] || "";
3229
3508
  let finalUuid = null;
3230
3509
  if (suffix) {
3231
- const existingMatch = existingSections.find((s) => s.id && s.id.includes(suffix));
3510
+ const existingMatch = existingSections.find(
3511
+ (s) => s.id && s.id.includes(suffix)
3512
+ );
3232
3513
  if (existingMatch) {
3233
3514
  finalUuid = existingMatch.id;
3234
3515
  } else {
@@ -3319,28 +3600,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
3319
3600
  function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
3320
3601
  const [width, setWidth] = (0, import_react3.useState)(initialWidth);
3321
3602
  const [isResizing, setIsResizing] = (0, import_react3.useState)(false);
3322
- const handlePointerDown = (0, import_react3.useCallback)((e) => {
3323
- e.preventDefault();
3324
- e.stopPropagation();
3325
- setIsResizing(true);
3326
- const startX = e.clientX;
3327
- const startWidth = width;
3328
- const originalUserSelect = document.body.style.userSelect;
3329
- document.body.style.userSelect = "none";
3330
- const handlePointerMove = (moveEvent) => {
3331
- const deltaX = startX - moveEvent.clientX;
3332
- const newWidth = Math.min(Math.max(startWidth + deltaX, minWidth), maxWidth);
3333
- setWidth(newWidth);
3334
- };
3335
- const handlePointerUp = () => {
3336
- setIsResizing(false);
3337
- document.body.style.userSelect = originalUserSelect;
3338
- document.removeEventListener("pointermove", handlePointerMove);
3339
- document.removeEventListener("pointerup", handlePointerUp);
3340
- };
3341
- document.addEventListener("pointermove", handlePointerMove);
3342
- document.addEventListener("pointerup", handlePointerUp);
3343
- }, [width, minWidth, maxWidth]);
3603
+ const handlePointerDown = (0, import_react3.useCallback)(
3604
+ (e) => {
3605
+ e.preventDefault();
3606
+ e.stopPropagation();
3607
+ setIsResizing(true);
3608
+ const startX = e.clientX;
3609
+ const startWidth = width;
3610
+ const originalUserSelect = document.body.style.userSelect;
3611
+ document.body.style.userSelect = "none";
3612
+ const handlePointerMove = (moveEvent) => {
3613
+ const deltaX = startX - moveEvent.clientX;
3614
+ const newWidth = Math.min(
3615
+ Math.max(startWidth + deltaX, minWidth),
3616
+ maxWidth
3617
+ );
3618
+ setWidth(newWidth);
3619
+ };
3620
+ const handlePointerUp = () => {
3621
+ setIsResizing(false);
3622
+ document.body.style.userSelect = originalUserSelect;
3623
+ document.removeEventListener("pointermove", handlePointerMove);
3624
+ document.removeEventListener("pointerup", handlePointerUp);
3625
+ };
3626
+ document.addEventListener("pointermove", handlePointerMove);
3627
+ document.addEventListener("pointerup", handlePointerUp);
3628
+ },
3629
+ [width, minWidth, maxWidth]
3630
+ );
3344
3631
  return { width, isResizing, handlePointerDown, setWidth };
3345
3632
  }
3346
3633
 
@@ -3477,9 +3764,9 @@ function CustomPropertyDisplay({
3477
3764
  };
3478
3765
  const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
3479
3766
  const handleListItemChange = (j, f, v) => setTempProp((p) => {
3480
- const newValue = [...p.value];
3481
- newValue[j] = { ...newValue[j], [f]: v };
3482
- return { ...p, value: newValue };
3767
+ const newValue2 = [...p.value];
3768
+ newValue2[j] = { ...newValue2[j], [f]: v };
3769
+ return { ...p, value: newValue2 };
3483
3770
  });
3484
3771
  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";
3485
3772
  const renderEditView = () => {
@@ -3533,14 +3820,14 @@ function CustomPropertyDisplay({
3533
3820
  const inputClass = `${baseInput} ${noSpinnerClass}`;
3534
3821
  const handleDateTypeChange = (newDateType) => {
3535
3822
  var _a3, _b2, _c2;
3536
- let newValue = { type: newDateType };
3823
+ let newValue2 = { type: newDateType };
3537
3824
  if (newDateType === "Date Interval") {
3538
- newValue.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3539
- newValue.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3825
+ newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
3826
+ newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
3540
3827
  } else {
3541
- newValue.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3828
+ newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
3542
3829
  }
3543
- handlePropChange("value", newValue);
3830
+ handlePropChange("value", newValue2);
3544
3831
  };
3545
3832
  const handleDateValueChange = (dateField, dateValue) => {
3546
3833
  handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
@@ -4388,7 +4675,7 @@ ${space}${bullet} `);
4388
4675
  }
4389
4676
  ),
4390
4677
  /* @__PURE__ */ import_react6.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
4391
- /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.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")),
4678
+ /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.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")),
4392
4679
  /* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ import_react6.default.createElement(
4393
4680
  "button",
4394
4681
  {
@@ -5503,7 +5790,7 @@ function AncestryRelationshipPanel({
5503
5790
  onImageClick: handleImageClickFromText,
5504
5791
  onSaveDescription: handleSaveDescriptionInline
5505
5792
  }
5506
- ) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div", null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react9.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement(
5793
+ ) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react9.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ import_react9.default.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__ */ import_react9.default.createElement(
5507
5794
  DescriptionDisplay,
5508
5795
  {
5509
5796
  description,
@@ -5808,6 +6095,7 @@ function CreateAncestryPanel({
5808
6095
  } = ancestryMode;
5809
6096
  const [isSaving, setIsSaving] = (0, import_react11.useState)(false);
5810
6097
  const [isLinkCopied, setIsLinkCopied] = (0, import_react11.useState)(false);
6098
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react11.useState)(false);
5811
6099
  const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = (0, import_react11.useState)(false);
5812
6100
  const handleCopyLink = (e) => {
5813
6101
  e.stopPropagation();
@@ -5875,12 +6163,14 @@ function CreateAncestryPanel({
5875
6163
  };
5876
6164
  const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
5877
6165
  setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
6166
+ setHasUnsavedChanges(true);
5878
6167
  };
5879
6168
  const handleToggleAddMode = (isAbstraction = false) => {
5880
6169
  if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
5881
6170
  setTargetRenderNodeId(null);
5882
6171
  }
5883
6172
  setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
6173
+ setHasUnsavedChanges(true);
5884
6174
  };
5885
6175
  const handleRemoveNode = (0, import_react11.useCallback)((pathToRemove, isAbstraction = false) => {
5886
6176
  if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
@@ -5898,6 +6188,7 @@ function CreateAncestryPanel({
5898
6188
  const indexToRemove = pathToRemove[pathToRemove.length - 1];
5899
6189
  if (currentParent.children && currentParent.children.length > indexToRemove) {
5900
6190
  currentParent.children.splice(indexToRemove, 1);
6191
+ setHasUnsavedChanges(true);
5901
6192
  }
5902
6193
  return { ...prev, [treeKey]: newTree };
5903
6194
  });
@@ -5956,6 +6247,7 @@ function CreateAncestryPanel({
5956
6247
  updateGlobalTree(rootTreeClone);
5957
6248
  }
5958
6249
  setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
6250
+ setHasUnsavedChanges(true);
5959
6251
  } else {
5960
6252
  alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
5961
6253
  }
@@ -6028,6 +6320,7 @@ function CreateAncestryPanel({
6028
6320
  const handleAddProp = () => {
6029
6321
  const newProp = createNewCustomProperty(customProps);
6030
6322
  setCustomProps((p) => [...p, newProp]);
6323
+ setHasUnsavedChanges(true);
6031
6324
  setTimeout(() => {
6032
6325
  var _a;
6033
6326
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -6036,11 +6329,13 @@ function CreateAncestryPanel({
6036
6329
  const handleRemoveProp = (i) => {
6037
6330
  const newProps = customProps.filter((_, idx) => idx !== i);
6038
6331
  setCustomProps(newProps);
6332
+ setHasUnsavedChanges(true);
6039
6333
  };
6040
6334
  const handleUpdateProp = (index, updatedProp) => {
6041
6335
  const newProps = [...customProps];
6042
6336
  newProps[index] = updatedProp;
6043
6337
  setCustomProps(newProps);
6338
+ setHasUnsavedChanges(true);
6044
6339
  };
6045
6340
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
6046
6341
  (0, import_react11.useEffect)(() => {
@@ -6150,6 +6445,7 @@ function CreateAncestryPanel({
6150
6445
  updateGlobalTree(rootTreeClone);
6151
6446
  setBranchStack([...branchStack]);
6152
6447
  setIsPickerOpen(false);
6448
+ setHasUnsavedChanges(true);
6153
6449
  try {
6154
6450
  setIsSaving(true);
6155
6451
  const rootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6163,6 +6459,7 @@ function CreateAncestryPanel({
6163
6459
  rootExtras
6164
6460
  );
6165
6461
  setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
6462
+ setHasUnsavedChanges(false);
6166
6463
  if (onRenderFullAncestry) {
6167
6464
  const fullTreePayload = {
6168
6465
  ancestry_id: ancestryMode.currentAncestryId || "temp_root",
@@ -6205,6 +6502,7 @@ function CreateAncestryPanel({
6205
6502
  if (branchIndex !== -1) {
6206
6503
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6207
6504
  updateGlobalTree(rootTreeClone);
6505
+ setHasUnsavedChanges(true);
6208
6506
  try {
6209
6507
  setIsSaving(true);
6210
6508
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6226,6 +6524,7 @@ function CreateAncestryPanel({
6226
6524
  isPrivate,
6227
6525
  ancestryMode.abstraction_tree
6228
6526
  ));
6527
+ setHasUnsavedChanges(false);
6229
6528
  if (onClearAncestryVisuals) {
6230
6529
  onClearAncestryVisuals(currentStep.branchId);
6231
6530
  }
@@ -6258,6 +6557,7 @@ function CreateAncestryPanel({
6258
6557
  if (branchIndex !== -1) {
6259
6558
  foundParentPath.node.parallel_branches.splice(branchIndex, 1);
6260
6559
  updateGlobalTree(rootTreeClone);
6560
+ setHasUnsavedChanges(true);
6261
6561
  try {
6262
6562
  setIsSaving(true);
6263
6563
  const currentRootProps = extractCustomPropsFromNode(ancestryMode);
@@ -6279,6 +6579,7 @@ function CreateAncestryPanel({
6279
6579
  isPrivate,
6280
6580
  ancestryMode.abstraction_tree
6281
6581
  ));
6582
+ setHasUnsavedChanges(false);
6282
6583
  if (onClearAncestryVisuals) {
6283
6584
  onClearAncestryVisuals(currentStep.branchId);
6284
6585
  }
@@ -6540,6 +6841,7 @@ function CreateAncestryPanel({
6540
6841
  }
6541
6842
  setBranchStack(parentStack);
6542
6843
  setTargetScrollSectionId(targetFocusId);
6844
+ setHasUnsavedChanges(true);
6543
6845
  if (onRenderFullAncestry) {
6544
6846
  const parentStack2 = currentStack;
6545
6847
  const rotation = parentStack2.reduce((acc, step) => {
@@ -6601,7 +6903,6 @@ function CreateAncestryPanel({
6601
6903
  direction,
6602
6904
  tree: {
6603
6905
  node: nodeData,
6604
- node_id: nodeId,
6605
6906
  children: [],
6606
6907
  relationship: {}
6607
6908
  }
@@ -6623,6 +6924,7 @@ function CreateAncestryPanel({
6623
6924
  savedMaxIndex: parentIndexToSave,
6624
6925
  entryDirection: direction
6625
6926
  }]);
6927
+ setHasUnsavedChanges(true);
6626
6928
  if (branch && branch.tree && onRenderFullAncestry) {
6627
6929
  const branchAncestryObj = {
6628
6930
  ancestry_id: branch.id,
@@ -6673,6 +6975,10 @@ function CreateAncestryPanel({
6673
6975
  const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
6674
6976
  const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
6675
6977
  const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
6978
+ if (!keepOpen && !hasUnsavedChanges) {
6979
+ onClose();
6980
+ return;
6981
+ }
6676
6982
  if (!currentInputName.trim()) {
6677
6983
  alert("O nome n\xE3o pode estar vazio.");
6678
6984
  return;
@@ -6680,11 +6986,7 @@ function CreateAncestryPanel({
6680
6986
  setIsSaving(true);
6681
6987
  const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
6682
6988
  setExistingSections(processedSections);
6683
- const updatedRootTree = applyDescriptionToTree(
6684
- ancestryMode.tree,
6685
- currentInputDesc,
6686
- processedSections
6687
- );
6989
+ const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
6688
6990
  const extrasObj = {
6689
6991
  ...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
6690
6992
  is_private: isPrivate
@@ -6726,6 +7028,7 @@ function CreateAncestryPanel({
6726
7028
  isPrivate,
6727
7029
  ancestryMode.abstraction_tree
6728
7030
  ));
7031
+ setHasUnsavedChanges(false);
6729
7032
  if (onRenderFullAncestry) {
6730
7033
  const rotation = branchStack.reduce((acc, step) => {
6731
7034
  return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
@@ -6777,6 +7080,7 @@ function CreateAncestryPanel({
6777
7080
  updatedRootTree,
6778
7081
  extrasObj
6779
7082
  );
7083
+ setHasUnsavedChanges(false);
6780
7084
  setLastSavedSnapshot(takeSnapshot(
6781
7085
  updatedRootTree,
6782
7086
  currentInputName,
@@ -6799,6 +7103,7 @@ function CreateAncestryPanel({
6799
7103
  const newTreeString = JSON.stringify(newRootTree);
6800
7104
  if (currentTreeString !== newTreeString) {
6801
7105
  updateGlobalTree(newRootTree);
7106
+ setHasUnsavedChanges(true);
6802
7107
  }
6803
7108
  }, [description, existingSections]);
6804
7109
  const handleTriggerFullRender = () => {
@@ -6821,6 +7126,7 @@ function CreateAncestryPanel({
6821
7126
  };
6822
7127
  const handleSaveDescriptionInline = (newDesc) => {
6823
7128
  setDescription(newDesc);
7129
+ setHasUnsavedChanges(true);
6824
7130
  handleLocalSave(true, { description: newDesc });
6825
7131
  };
6826
7132
  const swallow = (e) => e.stopPropagation();
@@ -6950,7 +7256,11 @@ function CreateAncestryPanel({
6950
7256
  {
6951
7257
  type: "text",
6952
7258
  value: ancestryName,
6953
- onChange: (e) => setAncestryName(e.target.value),
7259
+ onChange: (e) => {
7260
+ setAncestryName(e.target.value);
7261
+ setHasUnsavedChanges(true);
7262
+ },
7263
+ readOnly: isContextLinked,
6954
7264
  placeholder: "Nome da Ancestralidade",
6955
7265
  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"
6956
7266
  }
@@ -7438,7 +7748,7 @@ function ColorPicker({ color, onChange, disabled }) {
7438
7748
  style: { backgroundColor: preset },
7439
7749
  title: preset
7440
7750
  },
7441
- color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ import_react13.default.createElement(import_fi11.FiCheck, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
7751
+ (color || "").toLowerCase() === (preset || "").toLowerCase() && /* @__PURE__ */ import_react13.default.createElement(import_fi11.FiCheck, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
7442
7752
  )))), /* @__PURE__ */ import_react13.default.createElement("style", null, `
7443
7753
  .custom-react-colorful .react-colorful {
7444
7754
  width: 100%;
@@ -7530,13 +7840,23 @@ function InSceneCreationForm({
7530
7840
  }, [hasImages, useImageAsTexture, onImageChange]);
7531
7841
  (0, import_react14.useEffect)(() => {
7532
7842
  let result = [];
7843
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
7533
7844
  if (typeInput.trim() === "") {
7534
- result = existingTypes.filter((t) => !types.includes(t));
7845
+ result = validExistingTypes.filter((t) => !types.includes(t));
7535
7846
  } else {
7536
- const lowercasedInput = typeInput.toLowerCase();
7537
- result = existingTypes.filter(
7538
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
7539
- );
7847
+ console.log("InSceneCreationForm: Filtrando tipos com input:", typeInput);
7848
+ try {
7849
+ const lowercasedInput = typeInput.toLowerCase();
7850
+ result = validExistingTypes.filter((t) => {
7851
+ if (!t) {
7852
+ console.warn("InSceneCreationForm: Tipo encontrado como undefined/null durante filtragem");
7853
+ return false;
7854
+ }
7855
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
7856
+ });
7857
+ } catch (err) {
7858
+ console.error("InSceneCreationForm: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
7859
+ }
7540
7860
  }
7541
7861
  if (sourceTypes) {
7542
7862
  const priorityTypes = Array.isArray(sourceTypes) ? sourceTypes : [sourceTypes];
@@ -7599,9 +7919,9 @@ function InSceneCreationForm({
7599
7919
  };
7600
7920
  const handleToggleImageMode = () => {
7601
7921
  var _a2, _b;
7602
- const newValue = !useImageAsTexture;
7603
- setUseImageAsTexture(newValue);
7604
- if (newValue) {
7922
+ const newValue2 = !useImageAsTexture;
7923
+ setUseImageAsTexture(newValue2);
7924
+ if (newValue2) {
7605
7925
  const firstImageProp = customProps.find((p) => p.type === "images");
7606
7926
  if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
7607
7927
  const url = firstImageProp.value[0].value;
@@ -7899,9 +8219,9 @@ function InSceneVersionForm({
7899
8219
  };
7900
8220
  const handleToggleImageMode = () => {
7901
8221
  var _a, _b;
7902
- const newValue = !useImageAsTexture;
7903
- setUseImageAsTexture(newValue);
7904
- if (newValue) {
8222
+ const newValue2 = !useImageAsTexture;
8223
+ setUseImageAsTexture(newValue2);
8224
+ if (newValue2) {
7905
8225
  const firstImageProp = customProps.find((p) => p.type === "images");
7906
8226
  if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
7907
8227
  const url = firstImageProp.value[0].value;
@@ -8077,10 +8397,13 @@ function InSceneQuestForm({
8077
8397
  onSizeChange,
8078
8398
  viewName = "Projeto",
8079
8399
  // NOVA PROP
8080
- questCounter = 1
8400
+ questCounter = 1,
8081
8401
  // NOVA PROP
8402
+ viewMembers = []
8082
8403
  }) {
8404
+ var _a, _b;
8083
8405
  const [name, setName] = (0, import_react16.useState)("");
8406
+ const [assigneeId, setAssigneeId] = (0, import_react16.useState)("");
8084
8407
  const [types, setTypes] = (0, import_react16.useState)(["quest"]);
8085
8408
  const [typeInput, setTypeInput] = (0, import_react16.useState)("");
8086
8409
  const [status, setStatus] = (0, import_react16.useState)("Backlog");
@@ -8088,6 +8411,8 @@ function InSceneQuestForm({
8088
8411
  const [intensity, setIntensity] = (0, import_react16.useState)(0);
8089
8412
  const [description, setDescription] = (0, import_react16.useState)("");
8090
8413
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react16.useState)(false);
8414
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react16.useState)(false);
8415
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react16.useState)("");
8091
8416
  const [customProps, setCustomProps] = (0, import_react16.useState)([]);
8092
8417
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react16.useState)(false);
8093
8418
  const propsEndRef = (0, import_react16.useRef)(null);
@@ -8096,8 +8421,8 @@ function InSceneQuestForm({
8096
8421
  const newProp = createNewCustomProperty(customProps);
8097
8422
  setCustomProps([...customProps, newProp]);
8098
8423
  setTimeout(() => {
8099
- var _a;
8100
- (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
8424
+ var _a2;
8425
+ (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
8101
8426
  }, 100);
8102
8427
  };
8103
8428
  const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
@@ -8143,6 +8468,7 @@ function InSceneQuestForm({
8143
8468
  type: types,
8144
8469
  color: QUEST_STATUS_COLORS2[status],
8145
8470
  status,
8471
+ assignee_id: assigneeId || null,
8146
8472
  size,
8147
8473
  intensity,
8148
8474
  description: description.trim(),
@@ -8214,7 +8540,59 @@ function InSceneQuestForm({
8214
8540
  },
8215
8541
  /* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
8216
8542
  s
8217
- )))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.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__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
8543
+ )))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react16.default.createElement(
8544
+ "button",
8545
+ {
8546
+ type: "button",
8547
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
8548
+ 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"
8549
+ },
8550
+ /* @__PURE__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _a.name) || ((_b = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _b.email) || "Nenhum")),
8551
+ /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
8552
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
8553
+ setIsAssigneeDropdownOpen(false);
8554
+ setAssigneeSearchQuery("");
8555
+ } }), /* @__PURE__ */ import_react16.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react16.default.createElement(
8556
+ "input",
8557
+ {
8558
+ type: "text",
8559
+ autoFocus: true,
8560
+ placeholder: "Buscar membro...",
8561
+ value: assigneeSearchQuery,
8562
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
8563
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
8564
+ onClick: (e) => e.stopPropagation()
8565
+ }
8566
+ )), /* @__PURE__ */ import_react16.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react16.default.createElement(
8567
+ "li",
8568
+ {
8569
+ onClick: () => {
8570
+ setAssigneeId("");
8571
+ setIsAssigneeDropdownOpen(false);
8572
+ setAssigneeSearchQuery("");
8573
+ },
8574
+ 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"}`
8575
+ },
8576
+ "Nenhum"
8577
+ ), viewMembers.filter((member) => {
8578
+ const search = assigneeSearchQuery.toLowerCase();
8579
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8580
+ }).map((member) => /* @__PURE__ */ import_react16.default.createElement(
8581
+ "li",
8582
+ {
8583
+ key: member.id,
8584
+ onClick: () => {
8585
+ setAssigneeId(member.id);
8586
+ setIsAssigneeDropdownOpen(false);
8587
+ setAssigneeSearchQuery("");
8588
+ },
8589
+ 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"}`
8590
+ },
8591
+ member.name || member.email || member.id
8592
+ )), viewMembers.filter((member) => {
8593
+ const search = assigneeSearchQuery.toLowerCase();
8594
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
8595
+ }).length === 0 && assigneeSearchQuery && /* @__PURE__ */ import_react16.default.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado")))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.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__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
8218
8596
  "input",
8219
8597
  {
8220
8598
  type: "text",
@@ -8321,6 +8699,7 @@ function NodeDetailsPanel({
8321
8699
  return !!(node == null ? void 0 : node.useImageAsTexture);
8322
8700
  });
8323
8701
  const [selectedImageUrl, setSelectedImageUrl] = (0, import_react17.useState)((node == null ? void 0 : node.textureImageUrl) ?? null);
8702
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react17.useState)(false);
8324
8703
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8325
8704
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8326
8705
  initialWidth: isReadMode ? 700 : 440,
@@ -8358,6 +8737,7 @@ function NodeDetailsPanel({
8358
8737
  else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
8359
8738
  else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
8360
8739
  setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
8740
+ setHasUnsavedChanges(false);
8361
8741
  }
8362
8742
  }, [node]);
8363
8743
  const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
@@ -8368,15 +8748,22 @@ function NodeDetailsPanel({
8368
8748
  }
8369
8749
  }, [hasImages, useImageAsTexture]);
8370
8750
  (0, import_react17.useEffect)(() => {
8751
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8371
8752
  if (typeInput.trim() === "") {
8372
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
8753
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8373
8754
  } else {
8374
- const lowercasedInput = typeInput.toLowerCase();
8375
- setFilteredTypes(
8376
- existingTypes.filter(
8377
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8378
- )
8379
- );
8755
+ console.log("NodeDetailsPanel: Filtrando tipos com input:", typeInput);
8756
+ try {
8757
+ const lowercasedInput = typeInput.toLowerCase();
8758
+ setFilteredTypes(
8759
+ validExistingTypes.filter((t) => {
8760
+ if (!t) return false;
8761
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
8762
+ })
8763
+ );
8764
+ } catch (err) {
8765
+ console.error("NodeDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
8766
+ }
8380
8767
  }
8381
8768
  }, [typeInput, existingTypes, types]);
8382
8769
  const handleIntensityChangeLocal = (e) => {
@@ -8384,6 +8771,7 @@ function NodeDetailsPanel({
8384
8771
  setIntensity(val);
8385
8772
  onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
8386
8773
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
8774
+ setHasUnsavedChanges(true);
8387
8775
  };
8388
8776
  const handleCopyLink = () => {
8389
8777
  if (!(node == null ? void 0 : node.id)) return;
@@ -8401,14 +8789,17 @@ function NodeDetailsPanel({
8401
8789
  const v = e.target.value;
8402
8790
  setName(v);
8403
8791
  onNameChange == null ? void 0 : onNameChange(node.id, v);
8792
+ setHasUnsavedChanges(true);
8404
8793
  };
8405
8794
  const handleColorChange = (val) => {
8406
8795
  setColor(val);
8407
8796
  onColorChange == null ? void 0 : onColorChange(node.id, val);
8797
+ setHasUnsavedChanges(true);
8408
8798
  };
8409
8799
  const handleSizeChange = (newSize) => {
8410
8800
  setSize(newSize);
8411
8801
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
8802
+ setHasUnsavedChanges(true);
8412
8803
  };
8413
8804
  const handleAddType = (newType) => {
8414
8805
  const trimmed = newType.trim();
@@ -8416,10 +8807,12 @@ function NodeDetailsPanel({
8416
8807
  setTypes([...types, trimmed]);
8417
8808
  setTypeInput("");
8418
8809
  setShowTypeSuggestions(false);
8810
+ setHasUnsavedChanges(true);
8419
8811
  }
8420
8812
  };
8421
8813
  const handleRemoveType = (indexToRemove) => {
8422
8814
  setTypes(types.filter((_, index) => index !== indexToRemove));
8815
+ setHasUnsavedChanges(true);
8423
8816
  };
8424
8817
  const handleTypeInputKeyDown = (e) => {
8425
8818
  if (e.key === "Enter") {
@@ -8432,6 +8825,7 @@ function NodeDetailsPanel({
8432
8825
  const handleAddProp = () => {
8433
8826
  const newProp = createNewCustomProperty(customProps);
8434
8827
  setCustomProps((p) => [...p, newProp]);
8828
+ setHasUnsavedChanges(true);
8435
8829
  setTimeout(() => {
8436
8830
  var _a;
8437
8831
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8440,19 +8834,21 @@ function NodeDetailsPanel({
8440
8834
  const handleRemoveProp = (i) => {
8441
8835
  const newProps = customProps.filter((_, idx) => idx !== i);
8442
8836
  setCustomProps(newProps);
8837
+ setHasUnsavedChanges(true);
8443
8838
  triggerAutoSave({ customProps: newProps });
8444
8839
  };
8445
8840
  const handleUpdateProp = (index, updatedProp) => {
8446
8841
  const newProps = [...customProps];
8447
8842
  newProps[index] = updatedProp;
8448
8843
  setCustomProps(newProps);
8844
+ setHasUnsavedChanges(true);
8449
8845
  if (!updatedProp.isEditing) {
8450
8846
  triggerAutoSave({ customProps: newProps });
8451
8847
  }
8452
8848
  };
8453
8849
  const handleToggleImageMode = () => {
8454
- const newValue = !useImageAsTexture;
8455
8850
  setUseImageAsTexture(newValue);
8851
+ setHasUnsavedChanges(true);
8456
8852
  let activeUrl = null;
8457
8853
  if (newValue) {
8458
8854
  const firstImageProp = customProps.find((p) => p.type === "images");
@@ -8474,6 +8870,7 @@ function NodeDetailsPanel({
8474
8870
  };
8475
8871
  const handleSelectTexture = (url) => {
8476
8872
  setSelectedImageUrl(url);
8873
+ setHasUnsavedChanges(true);
8477
8874
  onImageChange == null ? void 0 : onImageChange(true, url, color);
8478
8875
  onDataUpdate == null ? void 0 : onDataUpdate({
8479
8876
  ...node,
@@ -8484,6 +8881,7 @@ function NodeDetailsPanel({
8484
8881
  };
8485
8882
  const handleSaveDescriptionInline = (newDescription) => {
8486
8883
  setDescription(newDescription);
8884
+ setHasUnsavedChanges(true);
8487
8885
  onDataUpdate({ ...node, description: newDescription });
8488
8886
  triggerAutoSave({ description: newDescription });
8489
8887
  };
@@ -8494,6 +8892,10 @@ function NodeDetailsPanel({
8494
8892
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8495
8893
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8496
8894
  const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
8895
+ if (!keepOpen && !hasUnsavedChanges) {
8896
+ onClose();
8897
+ return;
8898
+ }
8497
8899
  if (!currentName.trim() || currentTypes.length === 0) {
8498
8900
  alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8499
8901
  return;
@@ -8518,6 +8920,7 @@ function NodeDetailsPanel({
8518
8920
  };
8519
8921
  await onSave(dataToSave, keepOpen);
8520
8922
  onDataUpdate(dataToSave);
8923
+ setHasUnsavedChanges(false);
8521
8924
  if (!keepOpen) {
8522
8925
  onClose();
8523
8926
  }
@@ -8584,7 +8987,7 @@ function NodeDetailsPanel({
8584
8987
  onImageClick: handleImageClickFromText,
8585
8988
  onSaveDescription: handleSaveDescriptionInline
8586
8989
  }
8587
- ) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div", null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react17.default.createElement(
8990
+ ) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react17.default.createElement(
8588
8991
  "button",
8589
8992
  {
8590
8993
  onClick: handleCopyLink,
@@ -8592,7 +8995,7 @@ function NodeDetailsPanel({
8592
8995
  title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
8593
8996
  },
8594
8997
  isLinkCopied ? /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiLink, { size: 12 })
8595
- )), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.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__ */ import_react17.default.createElement(
8998
+ )), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.default.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__ */ import_react17.default.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__ */ import_react17.default.createElement(
8596
8999
  "button",
8597
9000
  {
8598
9001
  type: "button",
@@ -8754,6 +9157,7 @@ function NodeDetailsPanel({
8754
9157
  initialValue: description,
8755
9158
  onSave: (newDescription) => {
8756
9159
  setDescription(newDescription);
9160
+ setHasUnsavedChanges(true);
8757
9161
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
8758
9162
  triggerAutoSave({ description: newDescription });
8759
9163
  },
@@ -8790,7 +9194,8 @@ function QuestDetailsPanel({
8790
9194
  onMentionClick,
8791
9195
  onUploadFile,
8792
9196
  userRole,
8793
- currentDatasetName
9197
+ currentDatasetName,
9198
+ viewMembers = []
8794
9199
  }) {
8795
9200
  var _a;
8796
9201
  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) || "";
@@ -8802,9 +9207,12 @@ function QuestDetailsPanel({
8802
9207
  const [typeInput, setTypeInput] = (0, import_react18.useState)("");
8803
9208
  const [status, setStatus] = (0, import_react18.useState)((node == null ? void 0 : node.status) ?? "Backlog");
8804
9209
  const [size, setSize] = (0, import_react18.useState)((node == null ? void 0 : node.size) ?? "medium");
9210
+ const [assigneeId, setAssigneeId] = (0, import_react18.useState)((node == null ? void 0 : node.assignee_id) || "");
8805
9211
  const [description, setDescription] = (0, import_react18.useState)((node == null ? void 0 : node.description) ?? "");
8806
9212
  const [intensity, setIntensity] = (0, import_react18.useState)((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8807
9213
  const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react18.useState)(false);
9214
+ const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react18.useState)(false);
9215
+ const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react18.useState)("");
8808
9216
  const [customProps, setCustomProps] = (0, import_react18.useState)(() => extractCustomPropsFromNode(node || {}));
8809
9217
  const [showTypeSuggestions, setShowTypeSuggestions] = (0, import_react18.useState)(false);
8810
9218
  const [filteredTypes, setFilteredTypes] = (0, import_react18.useState)([]);
@@ -8813,6 +9221,7 @@ function QuestDetailsPanel({
8813
9221
  const [existingSections, setExistingSections] = (0, import_react18.useState)((node == null ? void 0 : node.description_sections) || []);
8814
9222
  const [isSaving, setIsSaving] = (0, import_react18.useState)(false);
8815
9223
  const [isLinkCopied, setIsLinkCopied] = (0, import_react18.useState)(false);
9224
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react18.useState)(false);
8816
9225
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
8817
9226
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
8818
9227
  initialWidth: isReadMode ? 700 : 440,
@@ -8840,22 +9249,34 @@ function QuestDetailsPanel({
8840
9249
  setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
8841
9250
  setStatus((node == null ? void 0 : node.status) ?? "Backlog");
8842
9251
  setSize((node == null ? void 0 : node.size) ?? "medium");
9252
+ setAssigneeId((node == null ? void 0 : node.assignee_id) || "");
8843
9253
  setDescription((node == null ? void 0 : node.description) ?? "");
8844
9254
  setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
8845
9255
  setExistingSections((node == null ? void 0 : node.description_sections) || []);
8846
9256
  setCustomProps(extractCustomPropsFromNode(node || {}));
9257
+ setHasUnsavedChanges(false);
8847
9258
  }
8848
9259
  }, [node]);
8849
9260
  (0, import_react18.useEffect)(() => {
9261
+ const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
8850
9262
  if (typeInput.trim() === "") {
8851
- setFilteredTypes(existingTypes.filter((t) => !types.includes(t)));
9263
+ setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
8852
9264
  } else {
8853
- const lowercasedInput = typeInput.toLowerCase();
8854
- setFilteredTypes(
8855
- existingTypes.filter(
8856
- (t) => t.toLowerCase().includes(lowercasedInput) && !types.includes(t)
8857
- )
8858
- );
9265
+ console.log("QuestDetailsPanel: Filtrando tipos com input:", typeInput);
9266
+ try {
9267
+ const lowercasedInput = typeInput.toLowerCase();
9268
+ setFilteredTypes(
9269
+ validExistingTypes.filter((t) => {
9270
+ if (!t) {
9271
+ console.warn("QuestDetailsPanel: Tipo encontrado como undefined/null durante filtragem");
9272
+ return false;
9273
+ }
9274
+ return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
9275
+ })
9276
+ );
9277
+ } catch (err) {
9278
+ console.error("QuestDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
9279
+ }
8859
9280
  }
8860
9281
  }, [typeInput, existingTypes, types]);
8861
9282
  const handleCopyLink = () => {
@@ -8875,16 +9296,19 @@ function QuestDetailsPanel({
8875
9296
  setRawTitle(val);
8876
9297
  const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
8877
9298
  onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
9299
+ setHasUnsavedChanges(true);
8878
9300
  };
8879
9301
  const handleSizeChange = (newSize) => {
8880
9302
  setSize(newSize);
8881
9303
  onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
9304
+ setHasUnsavedChanges(true);
8882
9305
  };
8883
9306
  const handleStatusChange = (newStatus) => {
8884
9307
  setStatus(newStatus);
8885
9308
  const newColor = QUEST_STATUS_COLORS3[newStatus];
8886
9309
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
8887
9310
  onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
9311
+ setHasUnsavedChanges(true);
8888
9312
  };
8889
9313
  const handleAddType = (newType) => {
8890
9314
  const trimmed = newType.trim();
@@ -8892,11 +9316,13 @@ function QuestDetailsPanel({
8892
9316
  setTypes([...types, trimmed]);
8893
9317
  setTypeInput("");
8894
9318
  setShowTypeSuggestions(false);
9319
+ setHasUnsavedChanges(true);
8895
9320
  }
8896
9321
  };
8897
9322
  const handleRemoveType = (indexToRemove) => {
8898
9323
  if (types[indexToRemove] === "quest") return;
8899
9324
  setTypes(types.filter((_, index) => index !== indexToRemove));
9325
+ setHasUnsavedChanges(true);
8900
9326
  };
8901
9327
  const handleTypeInputKeyDown = (e) => {
8902
9328
  if (e.key === "Enter") {
@@ -8909,6 +9335,7 @@ function QuestDetailsPanel({
8909
9335
  const handleAddProp = () => {
8910
9336
  const newProp = createNewCustomProperty(customProps);
8911
9337
  setCustomProps((p) => [...p, newProp]);
9338
+ setHasUnsavedChanges(true);
8912
9339
  setTimeout(() => {
8913
9340
  var _a2;
8914
9341
  (_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -8917,18 +9344,21 @@ function QuestDetailsPanel({
8917
9344
  const handleRemoveProp = (i) => {
8918
9345
  const newProps = customProps.filter((_, idx) => idx !== i);
8919
9346
  setCustomProps(newProps);
9347
+ setHasUnsavedChanges(true);
8920
9348
  triggerAutoSave({ customProps: newProps });
8921
9349
  };
8922
9350
  const handleUpdateProp = (index, updatedProp) => {
8923
9351
  const newProps = [...customProps];
8924
9352
  newProps[index] = updatedProp;
8925
9353
  setCustomProps(newProps);
9354
+ setHasUnsavedChanges(true);
8926
9355
  if (!updatedProp.isEditing) {
8927
9356
  triggerAutoSave({ customProps: newProps });
8928
9357
  }
8929
9358
  };
8930
9359
  const handleSaveDescriptionInline = (newDescription) => {
8931
9360
  setDescription(newDescription);
9361
+ setHasUnsavedChanges(true);
8932
9362
  onDataUpdate({ ...node, description: newDescription });
8933
9363
  triggerAutoSave({ description: newDescription });
8934
9364
  };
@@ -8936,10 +9366,15 @@ function QuestDetailsPanel({
8936
9366
  const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
8937
9367
  const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
8938
9368
  const currentTypes = overrides.types !== void 0 ? overrides.types : types;
9369
+ const currentAssigneeId = overrides.assigneeId !== void 0 ? overrides.assigneeId : assigneeId;
8939
9370
  const currentDescription = overrides.description !== void 0 ? overrides.description : description;
8940
9371
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
8941
9372
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
8942
9373
  const currentStatus = overrides.status !== void 0 ? overrides.status : status;
9374
+ if (!keepOpen && !hasUnsavedChanges) {
9375
+ onClose();
9376
+ return;
9377
+ }
8943
9378
  if (!currentRawTitle.trim() || currentTypes.length === 0) {
8944
9379
  alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
8945
9380
  return;
@@ -8957,6 +9392,7 @@ function QuestDetailsPanel({
8957
9392
  type: currentTypes,
8958
9393
  color: QUEST_STATUS_COLORS3[currentStatus],
8959
9394
  status: currentStatus,
9395
+ assignee_id: currentAssigneeId || null,
8960
9396
  size,
8961
9397
  description: currentDescription,
8962
9398
  description_sections: processedSections,
@@ -8969,6 +9405,7 @@ function QuestDetailsPanel({
8969
9405
  };
8970
9406
  await onSave(dataToSave, keepOpen);
8971
9407
  onDataUpdate(dataToSave);
9408
+ setHasUnsavedChanges(false);
8972
9409
  if (!keepOpen) {
8973
9410
  onClose();
8974
9411
  }
@@ -8988,6 +9425,8 @@ function QuestDetailsPanel({
8988
9425
  onClose();
8989
9426
  };
8990
9427
  const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
9428
+ const assigneeMember = viewMembers.find((m) => m.id === assigneeId);
9429
+ const isAssigneeUndefined = assigneeId && !assigneeMember;
8991
9430
  return /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement(
8992
9431
  "div",
8993
9432
  {
@@ -9026,7 +9465,7 @@ function QuestDetailsPanel({
9026
9465
  onImageClick: handleImageClickFromText,
9027
9466
  onSaveDescription: handleSaveDescriptionInline
9028
9467
  }
9029
- ) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div", null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement(import_fi16.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiLink, { size: 12 }))), /* @__PURE__ */ import_react18.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ import_react18.default.createElement(
9468
+ ) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement(import_fi16.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiLink, { size: 12 }))), /* @__PURE__ */ import_react18.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ import_react18.default.createElement(
9030
9469
  "input",
9031
9470
  {
9032
9471
  type: "text",
@@ -9057,7 +9496,71 @@ function QuestDetailsPanel({
9057
9496
  },
9058
9497
  /* @__PURE__ */ import_react18.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS3[s] } }),
9059
9498
  s
9060
- )))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.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__ */ import_react18.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiX, { size: 12 })))), canEdit && /* @__PURE__ */ import_react18.default.createElement(
9499
+ )))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), canEdit ? /* @__PURE__ */ import_react18.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react18.default.createElement(
9500
+ "button",
9501
+ {
9502
+ type: "button",
9503
+ onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
9504
+ 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"
9505
+ },
9506
+ /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("span", { className: "text-slate-200 font-medium" }, isAssigneeUndefined ? "Undefined" : (assigneeMember == null ? void 0 : assigneeMember.name) || (assigneeMember == null ? void 0 : assigneeMember.email) || "Nenhum")),
9507
+ /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
9508
+ ), isAssigneeDropdownOpen && /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
9509
+ setIsAssigneeDropdownOpen(false);
9510
+ setAssigneeSearchQuery("");
9511
+ } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react18.default.createElement(
9512
+ "input",
9513
+ {
9514
+ type: "text",
9515
+ autoFocus: true,
9516
+ placeholder: "Buscar membro...",
9517
+ value: assigneeSearchQuery,
9518
+ onChange: (e) => setAssigneeSearchQuery(e.target.value),
9519
+ className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
9520
+ onClick: (e) => e.stopPropagation()
9521
+ }
9522
+ )), /* @__PURE__ */ import_react18.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement(
9523
+ "li",
9524
+ {
9525
+ onClick: () => {
9526
+ setAssigneeId("");
9527
+ setHasUnsavedChanges(true);
9528
+ setIsAssigneeDropdownOpen(false);
9529
+ setAssigneeSearchQuery("");
9530
+ },
9531
+ 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"}`
9532
+ },
9533
+ "Nenhum"
9534
+ ), isAssigneeUndefined && /* @__PURE__ */ import_react18.default.createElement(
9535
+ "li",
9536
+ {
9537
+ onClick: () => {
9538
+ setIsAssigneeDropdownOpen(false);
9539
+ setAssigneeSearchQuery("");
9540
+ },
9541
+ className: "px-3 py-2.5 text-sm cursor-pointer bg-red-500/10 text-red-300 flex items-center gap-2"
9542
+ },
9543
+ "Undefined (Removido)"
9544
+ ), viewMembers.filter((member) => {
9545
+ const search = assigneeSearchQuery.toLowerCase();
9546
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9547
+ }).map((member) => /* @__PURE__ */ import_react18.default.createElement(
9548
+ "li",
9549
+ {
9550
+ key: member.id,
9551
+ onClick: () => {
9552
+ setAssigneeId(member.id);
9553
+ setHasUnsavedChanges(true);
9554
+ setIsAssigneeDropdownOpen(false);
9555
+ setAssigneeSearchQuery("");
9556
+ },
9557
+ 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"}`
9558
+ },
9559
+ member.name || member.email || member.id
9560
+ )), viewMembers.filter((member) => {
9561
+ const search = assigneeSearchQuery.toLowerCase();
9562
+ return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
9563
+ }).length === 0 && assigneeSearchQuery && /* @__PURE__ */ import_react18.default.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado"))))) : /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.createElement(import_fi16.FiUser, { className: "opacity-50", size: 14 }), assigneeId ? assigneeMember ? assigneeMember.name || assigneeMember.email : "Undefined" : "Nenhum")), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.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__ */ import_react18.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiX, { size: 12 })))), canEdit && /* @__PURE__ */ import_react18.default.createElement(
9061
9564
  "input",
9062
9565
  {
9063
9566
  type: "text",
@@ -9200,9 +9703,12 @@ function RelationshipDetailsPanel({
9200
9703
  const [description, setDescription] = (0, import_react20.useState)((link == null ? void 0 : link.description) ?? "");
9201
9704
  const [customProps, setCustomProps] = (0, import_react20.useState)(() => extractCustomPropsFromNode(link || {}));
9202
9705
  const [existingSections, setExistingSections] = (0, import_react20.useState)((link == null ? void 0 : link.description_sections) || []);
9706
+ const [sourceLabel, setSourceLabel] = (0, import_react20.useState)((link == null ? void 0 : link.source_label) ?? "");
9707
+ const [targetLabel, setTargetLabel] = (0, import_react20.useState)((link == null ? void 0 : link.target_label) ?? "");
9203
9708
  const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react20.useState)(false);
9204
9709
  const [isSaving, setIsSaving] = (0, import_react20.useState)(false);
9205
9710
  const [isReadMode, setIsReadMode] = (0, import_react20.useState)(false);
9711
+ const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react20.useState)(false);
9206
9712
  const propsEndRef = (0, import_react20.useRef)(null);
9207
9713
  const canEdit = (0, import_react20.useMemo)(() => {
9208
9714
  const ability = defineAbilityFor(userRole);
@@ -9213,12 +9719,16 @@ function RelationshipDetailsPanel({
9213
9719
  setDescription((link == null ? void 0 : link.description) ?? "");
9214
9720
  setExistingSections((link == null ? void 0 : link.description_sections) || []);
9215
9721
  setCustomProps(extractCustomPropsFromNode(link || {}));
9722
+ setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
9723
+ setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
9724
+ setHasUnsavedChanges(false);
9216
9725
  }, [link]);
9217
9726
  const swallow = (e) => e.stopPropagation();
9218
9727
  const handleAddProp = () => {
9219
9728
  if (!canEdit) return;
9220
9729
  const newProp = createNewCustomProperty(customProps);
9221
9730
  setCustomProps((p) => [...p, newProp]);
9731
+ setHasUnsavedChanges(true);
9222
9732
  setTimeout(() => {
9223
9733
  var _a;
9224
9734
  (_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
@@ -9230,6 +9740,12 @@ function RelationshipDetailsPanel({
9230
9740
  const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
9231
9741
  const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
9232
9742
  const currentName = overrides.name !== void 0 ? overrides.name : name;
9743
+ const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
9744
+ const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
9745
+ if (!keepOpen && !hasUnsavedChanges) {
9746
+ onClose();
9747
+ return;
9748
+ }
9233
9749
  setIsSaving(true);
9234
9750
  try {
9235
9751
  const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
@@ -9245,8 +9761,11 @@ function RelationshipDetailsPanel({
9245
9761
  isCurved: link.isCurved,
9246
9762
  curveOffset: link.curveOffset
9247
9763
  };
9764
+ if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
9765
+ if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
9248
9766
  await onSave(dataToSave, keepOpen);
9249
9767
  onDataUpdate(dataToSave);
9768
+ setHasUnsavedChanges(false);
9250
9769
  if (!keepOpen) {
9251
9770
  onClose();
9252
9771
  }
@@ -9260,18 +9779,21 @@ function RelationshipDetailsPanel({
9260
9779
  const handleSaveDescriptionInline = (newDescription) => {
9261
9780
  if (!canEdit) return;
9262
9781
  setDescription(newDescription);
9782
+ setHasUnsavedChanges(true);
9263
9783
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9264
9784
  triggerAutoSave({ description: newDescription });
9265
9785
  };
9266
9786
  const handleRemoveProp = (i) => {
9267
9787
  const newProps = customProps.filter((_, idx) => idx !== i);
9268
9788
  setCustomProps(newProps);
9789
+ setHasUnsavedChanges(true);
9269
9790
  triggerAutoSave({ customProps: newProps });
9270
9791
  };
9271
9792
  const handleUpdateProp = (index, updatedProp) => {
9272
9793
  const newProps = [...customProps];
9273
9794
  newProps[index] = updatedProp;
9274
9795
  setCustomProps(newProps);
9796
+ setHasUnsavedChanges(true);
9275
9797
  if (!updatedProp.isEditing) {
9276
9798
  triggerAutoSave({ customProps: newProps });
9277
9799
  }
@@ -9319,19 +9841,52 @@ function RelationshipDetailsPanel({
9319
9841
  onImageClick: handleImageClickFromText,
9320
9842
  onSaveDescription: handleSaveDescriptionInline
9321
9843
  }
9322
- ) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div", null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react20.default.createElement(
9844
+ ) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react20.default.createElement(
9323
9845
  "input",
9324
9846
  {
9325
9847
  type: "text",
9326
9848
  value: name,
9327
- onChange: (e) => setName(e.target.value),
9849
+ onChange: (e) => {
9850
+ setName(e.target.value);
9851
+ setHasUnsavedChanges(true);
9852
+ },
9328
9853
  placeholder: "Ex: Controla, Pertence a, Fornece...",
9329
9854
  disabled: !canEdit,
9330
9855
  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
9331
9856
  ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9332
9857
  `
9333
9858
  }
9334
- )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement(
9859
+ )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.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__ */ import_react20.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ import_react20.default.createElement(
9860
+ "input",
9861
+ {
9862
+ type: "text",
9863
+ value: sourceLabel,
9864
+ onChange: (e) => {
9865
+ setSourceLabel(e.target.value);
9866
+ setHasUnsavedChanges(true);
9867
+ },
9868
+ placeholder: "Ex: Conceitos",
9869
+ disabled: !canEdit,
9870
+ 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
9871
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9872
+ `
9873
+ }
9874
+ )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ import_react20.default.createElement(
9875
+ "input",
9876
+ {
9877
+ type: "text",
9878
+ value: targetLabel,
9879
+ onChange: (e) => {
9880
+ setTargetLabel(e.target.value);
9881
+ setHasUnsavedChanges(true);
9882
+ },
9883
+ placeholder: "Ex: Refer\xEAncias",
9884
+ disabled: !canEdit,
9885
+ 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
9886
+ ${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
9887
+ `
9888
+ }
9889
+ )))), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react20.default.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__ */ import_react20.default.createElement(
9335
9890
  DescriptionDisplay,
9336
9891
  {
9337
9892
  description,
@@ -9401,6 +9956,7 @@ function RelationshipDetailsPanel({
9401
9956
  onSave: (newDescription) => {
9402
9957
  if (!canEdit) return;
9403
9958
  setDescription(newDescription);
9959
+ setHasUnsavedChanges(true);
9404
9960
  onDataUpdate((prev) => ({ ...prev, description: newDescription }));
9405
9961
  triggerAutoSave({ description: newDescription });
9406
9962
  },
@@ -9848,7 +10404,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
9848
10404
  onMentionClick,
9849
10405
  onImageClick: handleImageClickFromText
9850
10406
  }
9851
- ) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div", null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react24.default.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__ */ import_react24.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react24.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react24.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react24.default.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__ */ import_react24.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react24.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react24.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react24.default.createElement(
10407
+ ) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react24.default.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__ */ import_react24.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react24.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react24.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react24.default.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__ */ import_react24.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react24.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react24.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react24.default.createElement(
9852
10408
  "button",
9853
10409
  {
9854
10410
  onClick: () => setIsReadMode(true),
@@ -10487,7 +11043,8 @@ function XViewScene({
10487
11043
  save_ancestry_board_action,
10488
11044
  upload_file_action,
10489
11045
  delete_file_action,
10490
- check_user_permission
11046
+ check_user_permission,
11047
+ get_view_members
10491
11048
  }) {
10492
11049
  var _a, _b, _c, _d, _e, _f, _g, _h;
10493
11050
  const { data: session, status } = (0, import_react27.useSession)();
@@ -10520,6 +11077,13 @@ function XViewScene({
10520
11077
  } else {
10521
11078
  setPermissionStatus("denied");
10522
11079
  setIsLoading(false);
11080
+ return;
11081
+ }
11082
+ if (get_view_members) {
11083
+ const membersRes = await get_view_members(owner_id, type, id);
11084
+ if (membersRes.success && membersRes.members) {
11085
+ setViewMembers(membersRes.members);
11086
+ }
10523
11087
  }
10524
11088
  } catch (error) {
10525
11089
  console.error("Erro ao verificar permiss\xE3o:", error);
@@ -10560,6 +11124,7 @@ function XViewScene({
10560
11124
  const [isLoading, setIsLoading] = (0, import_react26.useState)(true);
10561
11125
  const [permissionStatus, setPermissionStatus] = (0, import_react26.useState)("loading");
10562
11126
  const [userPermissionRole, setUserPermissionRole] = (0, import_react26.useState)(null);
11127
+ const [viewMembers, setViewMembers] = (0, import_react26.useState)([]);
10563
11128
  const [isInitialized, setIsInitialized] = (0, import_react26.useState)(false);
10564
11129
  const [sceneVersion, setSceneVersion] = (0, import_react26.useState)(0);
10565
11130
  const [contextMenu, setContextMenu] = (0, import_react26.useState)({
@@ -10628,6 +11193,7 @@ function XViewScene({
10628
11193
  });
10629
11194
  const [isImportModalOpen, setIsImportModalOpen] = (0, import_react26.useState)(false);
10630
11195
  const [importSuccessMessage, setImportSuccessMessage] = (0, import_react26.useState)("");
11196
+ const [invalidTargetError, setInvalidTargetError] = (0, import_react26.useState)(null);
10631
11197
  const [highlightedNodeId, setHighlightedNodeId] = (0, import_react26.useState)(null);
10632
11198
  const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react26.useState)(false);
10633
11199
  const [ancestryBoardData, setAncestryBoardData] = (0, import_react26.useState)([]);
@@ -12476,6 +13042,28 @@ function XViewScene({
12476
13042
  const handleStartVersioning = (nodeData) => {
12477
13043
  userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
12478
13044
  };
13045
+ const handleStartQuestQuick = (0, import_react26.useCallback)((questNode) => {
13046
+ var _a2;
13047
+ if (!questNode || !actionHandlerContext) return;
13048
+ const {
13049
+ labelObject,
13050
+ labelOffset,
13051
+ aura,
13052
+ borderRing,
13053
+ timelineIntervalBar,
13054
+ timelineEndLabel,
13055
+ ...cleanQuestNode
13056
+ } = questNode;
13057
+ const updatedNode = {
13058
+ ...cleanQuestNode,
13059
+ status: "In Progress",
13060
+ color: "#eab308",
13061
+ assignee_id: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id
13062
+ };
13063
+ if (userActionHandlers.handleSaveNodeDetails) {
13064
+ userActionHandlers.handleSaveNodeDetails(actionHandlerContext, updatedNode);
13065
+ }
13066
+ }, [session, actionHandlerContext]);
12479
13067
  const handleCancelQuest = (0, import_react26.useCallback)(() => {
12480
13068
  const { graphGroup, ghostElements } = stateRef.current;
12481
13069
  if (ghostElements.node && graphGroup) {
@@ -14160,7 +14748,13 @@ function XViewScene({
14160
14748
  if (!parentDataRef.current) {
14161
14749
  return [];
14162
14750
  }
14163
- return Object.values(parentDataRef.current).flatMap((fileData) => fileData.nodes).filter((node) => {
14751
+ return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
14752
+ const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
14753
+ return (fileData.nodes || []).map((node) => ({
14754
+ ...node,
14755
+ dataset_name: datasetName
14756
+ }));
14757
+ }).filter((node) => {
14164
14758
  var _a2;
14165
14759
  return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
14166
14760
  });
@@ -14304,6 +14898,9 @@ function XViewScene({
14304
14898
  }, 300);
14305
14899
  } else {
14306
14900
  setHasFocusedInitial(true);
14901
+ setInvalidTargetError(
14902
+ "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
14903
+ );
14307
14904
  }
14308
14905
  }
14309
14906
  }, [
@@ -14326,6 +14923,9 @@ function XViewScene({
14326
14923
  }, 300);
14327
14924
  } else {
14328
14925
  setHasOpenedInitialAncestry(true);
14926
+ setInvalidTargetError(
14927
+ "O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
14928
+ );
14329
14929
  }
14330
14930
  }
14331
14931
  }, [
@@ -14619,7 +15219,8 @@ function XViewScene({
14619
15219
  availableNodes: allAvailableNodes,
14620
15220
  availableAncestries: allAvailableAncestries,
14621
15221
  viewName: viewParams == null ? void 0 : viewParams.name,
14622
- questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1
15222
+ questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
15223
+ viewMembers
14623
15224
  }
14624
15225
  ),
14625
15226
  readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react26.default.createElement(
@@ -14749,7 +15350,8 @@ function XViewScene({
14749
15350
  onMentionClick: handleAddExistingNode,
14750
15351
  onUploadFile: upload_file_action,
14751
15352
  userRole: userPermissionRole,
14752
- currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
15353
+ currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
15354
+ viewMembers
14753
15355
  }
14754
15356
  ),
14755
15357
  detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react26.default.createElement(
@@ -14862,7 +15464,10 @@ function XViewScene({
14862
15464
  onRenderAncestry: handleStartReadingAncestry,
14863
15465
  onEditAncestry: handleEditAncestry,
14864
15466
  onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
14865
- onFocusNode: handleFocusNode
15467
+ onFocusNode: handleFocusNode,
15468
+ viewMembers,
15469
+ currentUser: session == null ? void 0 : session.user,
15470
+ onStartQuest: handleStartQuestQuick
14866
15471
  }
14867
15472
  ),
14868
15473
  /* @__PURE__ */ import_react26.default.createElement(
@@ -14935,6 +15540,83 @@ function XViewScene({
14935
15540
  currentViewName: viewParams == null ? void 0 : viewParams.name,
14936
15541
  currentAncestries: ancestryDataRef.current || []
14937
15542
  }
15543
+ ),
15544
+ invalidTargetError && /* @__PURE__ */ import_react26.default.createElement(
15545
+ "div",
15546
+ {
15547
+ className: "ui-overlay",
15548
+ style: {
15549
+ position: "fixed",
15550
+ top: "24px",
15551
+ left: "50%",
15552
+ transform: "translateX(-50%)",
15553
+ zIndex: 1e4,
15554
+ padding: "16px 24px",
15555
+ background: "rgba(30, 20, 20, 0.85)",
15556
+ backdropFilter: "blur(12px)",
15557
+ WebkitBackdropFilter: "blur(12px)",
15558
+ border: "1px solid rgba(255, 70, 70, 0.35)",
15559
+ borderRadius: "16px",
15560
+ boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
15561
+ color: "#ffa0a0",
15562
+ display: "flex",
15563
+ alignItems: "center",
15564
+ gap: "16px",
15565
+ fontFamily: "Inter, sans-serif",
15566
+ animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
15567
+ }
15568
+ },
15569
+ /* @__PURE__ */ import_react26.default.createElement("style", null, `
15570
+ @keyframes fadeInDown {
15571
+ from { opacity: 0; transform: translate(-50%, -20px); }
15572
+ to { opacity: 1; transform: translate(-50%, 0); }
15573
+ }
15574
+ `),
15575
+ /* @__PURE__ */ import_react26.default.createElement(
15576
+ "svg",
15577
+ {
15578
+ width: "20",
15579
+ height: "20",
15580
+ viewBox: "0 0 24 24",
15581
+ fill: "none",
15582
+ stroke: "currentColor",
15583
+ strokeWidth: "2",
15584
+ strokeLinecap: "round",
15585
+ strokeLinejoin: "round"
15586
+ },
15587
+ /* @__PURE__ */ import_react26.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
15588
+ /* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
15589
+ /* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
15590
+ ),
15591
+ /* @__PURE__ */ import_react26.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
15592
+ /* @__PURE__ */ import_react26.default.createElement(
15593
+ "button",
15594
+ {
15595
+ onClick: () => setInvalidTargetError(null),
15596
+ style: {
15597
+ background: "rgba(255, 255, 255, 0.1)",
15598
+ border: "none",
15599
+ color: "white",
15600
+ padding: "4px 10px",
15601
+ borderRadius: "8px",
15602
+ cursor: "pointer",
15603
+ fontSize: "12px",
15604
+ fontWeight: 600,
15605
+ transition: "all 0.2s",
15606
+ marginLeft: "8px",
15607
+ border: "1px solid rgba(255,255,255,0.1)"
15608
+ },
15609
+ onMouseOver: (e) => {
15610
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
15611
+ e.currentTarget.style.transform = "translateY(-1px)";
15612
+ },
15613
+ onMouseOut: (e) => {
15614
+ e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
15615
+ e.currentTarget.style.transform = "translateY(0)";
15616
+ }
15617
+ },
15618
+ "Fechar"
15619
+ )
14938
15620
  )
14939
15621
  );
14940
15622
  }
@@ -15361,6 +16043,17 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
15361
16043
  return { success: false, error: error.message };
15362
16044
  }
15363
16045
  }
16046
+ async function get_view_members_logic(db_services, ownerId, type, itemId) {
16047
+ try {
16048
+ if (!db_services.get_view_members) {
16049
+ return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
16050
+ }
16051
+ return await db_services.get_view_members(ownerId, type, itemId);
16052
+ } catch (error) {
16053
+ console.error("Erro em get_view_members_logic:", error);
16054
+ return { success: false, error: error.message };
16055
+ }
16056
+ }
15364
16057
  // Annotate the CommonJS export names for ESM import in node:
15365
16058
  0 && (module.exports = {
15366
16059
  XViewScene,
@@ -15369,6 +16062,7 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
15369
16062
  get_ancestry_file_logic,
15370
16063
  get_scene_view_data_logic,
15371
16064
  get_single_parent_file_logic,
16065
+ get_view_members_logic,
15372
16066
  import_parent_file_modal_get_logic,
15373
16067
  save_ancestry_board_logic,
15374
16068
  save_view_data_logic,