@lv-x-software-house/x_view 1.2.5-dev.2 → 1.2.5-dev.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +832 -155
- package/dist/index.mjs +836 -160
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -103,7 +103,10 @@ function ContextMenu({
|
|
|
103
103
|
onEditAncestry,
|
|
104
104
|
onDeleteAncestry,
|
|
105
105
|
onFocusNode,
|
|
106
|
-
onClose
|
|
106
|
+
onClose,
|
|
107
|
+
viewMembers,
|
|
108
|
+
currentUser,
|
|
109
|
+
onStartQuest
|
|
107
110
|
}) {
|
|
108
111
|
var _a, _b, _c;
|
|
109
112
|
const menuRef = useRef(null);
|
|
@@ -111,6 +114,7 @@ function ContextMenu({
|
|
|
111
114
|
const [menuView, setMenuView] = useState("main");
|
|
112
115
|
const [selectedAncestry, setSelectedAncestry] = useState(null);
|
|
113
116
|
const [versionSubMenu, setVersionSubMenu] = useState(null);
|
|
117
|
+
const [labelSubMenu, setLabelSubMenu] = useState(null);
|
|
114
118
|
const [isLinkCopied, setIsLinkCopied] = useState(false);
|
|
115
119
|
const [selectedQuestStatus, setSelectedQuestStatus] = useState(null);
|
|
116
120
|
const ability = useMemo(() => defineAbilityFor(userRole), [userRole]);
|
|
@@ -119,6 +123,7 @@ function ContextMenu({
|
|
|
119
123
|
setMenuView("main");
|
|
120
124
|
setSelectedAncestry(null);
|
|
121
125
|
setVersionSubMenu(null);
|
|
126
|
+
setLabelSubMenu(null);
|
|
122
127
|
setSelectedQuestStatus(null);
|
|
123
128
|
}
|
|
124
129
|
}, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
|
|
@@ -134,7 +139,7 @@ function ContextMenu({
|
|
|
134
139
|
if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
|
|
135
140
|
if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
|
|
136
141
|
setMenuPos({ left, top });
|
|
137
|
-
}, [data, menuView, versionSubMenu, selectedQuestStatus]);
|
|
142
|
+
}, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
|
|
138
143
|
useEffect(() => {
|
|
139
144
|
if (!data.visible) return;
|
|
140
145
|
const handleClickOutside = (e) => {
|
|
@@ -186,7 +191,21 @@ function ContextMenu({
|
|
|
186
191
|
var _a2;
|
|
187
192
|
return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
|
|
188
193
|
});
|
|
189
|
-
const
|
|
194
|
+
const getLabelForConnection = (conn) => {
|
|
195
|
+
if (conn.direction === "outgoing") return conn.link.source_label || null;
|
|
196
|
+
if (conn.direction === "incoming") return conn.link.target_label || null;
|
|
197
|
+
return null;
|
|
198
|
+
};
|
|
199
|
+
const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
|
|
200
|
+
const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
|
|
201
|
+
const labelGroups = commonWithLabel.reduce((acc, conn) => {
|
|
202
|
+
const label = getLabelForConnection(conn);
|
|
203
|
+
if (!acc[label]) acc[label] = [];
|
|
204
|
+
acc[label].push(conn);
|
|
205
|
+
return acc;
|
|
206
|
+
}, {});
|
|
207
|
+
const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
|
|
208
|
+
const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
|
|
190
209
|
var _a2;
|
|
191
210
|
const { targetNode } = conn;
|
|
192
211
|
const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
|
|
@@ -288,12 +307,25 @@ function ContextMenu({
|
|
|
288
307
|
});
|
|
289
308
|
};
|
|
290
309
|
const renderMainView = () => {
|
|
291
|
-
var _a2;
|
|
310
|
+
var _a2, _b2, _c2;
|
|
292
311
|
const hasVersions = computedVersions.length > 0;
|
|
293
312
|
const canCreateVersion = ability.can("create", "Versioning");
|
|
294
313
|
const canReadVersion = ability.can("read", "Versioning");
|
|
295
314
|
const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
|
|
296
|
-
|
|
315
|
+
const canStartQuest = isCurrentNodeQuest && (((_a2 = data.nodeData) == null ? void 0 : _a2.status) !== "In Progress" || ((_b2 = data.nodeData) == null ? void 0 : _b2.assignee_id) !== (currentUser == null ? void 0 : currentUser.id));
|
|
316
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, canStartQuest && /* @__PURE__ */ React.createElement(
|
|
317
|
+
"button",
|
|
318
|
+
{
|
|
319
|
+
onClick: () => {
|
|
320
|
+
onStartQuest == null ? void 0 : onStartQuest(data.nodeData);
|
|
321
|
+
onClose();
|
|
322
|
+
},
|
|
323
|
+
className: `w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md bg-yellow-500/10 text-yellow-400 hover:bg-yellow-500/20 hover:text-yellow-300 transition-colors duration-150 truncate font-semibold shadow-inner`,
|
|
324
|
+
title: "Atribuir a mim e iniciar"
|
|
325
|
+
},
|
|
326
|
+
/* @__PURE__ */ React.createElement("span", null, "\u{1F680}"),
|
|
327
|
+
/* @__PURE__ */ React.createElement("span", null, "Iniciar Quest")
|
|
328
|
+
), ability.can("create", "Connection") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_c2 = data.nodeData) == null ? void 0 : _c2.is_quest) && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ React.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ React.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ React.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ React.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ React.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ React.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ React.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ React.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ React.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
297
329
|
onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
|
|
298
330
|
onClose();
|
|
299
331
|
}, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ React.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ React.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ React.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ React.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ React.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ React.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ React.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss")), /* @__PURE__ */ React.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ React.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ React.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ React.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ React.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ React.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ React.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ React.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ React.createElement("span", null, "Excluir Node"))));
|
|
@@ -314,11 +346,32 @@ function ContextMenu({
|
|
|
314
346
|
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
315
347
|
))));
|
|
316
348
|
};
|
|
349
|
+
const renderLabelSubMenuView = () => {
|
|
350
|
+
const group = labelSubMenu;
|
|
351
|
+
const isScrollable = group.connections.length > 10;
|
|
352
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
353
|
+
setLabelSubMenu(null);
|
|
354
|
+
setMenuView("connections");
|
|
355
|
+
}, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ React.createElement(
|
|
356
|
+
"button",
|
|
357
|
+
{
|
|
358
|
+
key: conn.targetNode.id,
|
|
359
|
+
onClick: () => handleExpandAndClose([conn.link]),
|
|
360
|
+
className: baseButtonClass,
|
|
361
|
+
title: `Expandir conex\xE3o com ${conn.targetNode.name}`
|
|
362
|
+
},
|
|
363
|
+
/* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ React.createElement("polyline", { points: "12 5 19 12 12 19" })),
|
|
364
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
365
|
+
))));
|
|
366
|
+
};
|
|
317
367
|
const renderConnectionsView = () => {
|
|
318
368
|
if (versionSubMenu) {
|
|
319
369
|
return renderVersionSubMenuView();
|
|
320
370
|
}
|
|
321
|
-
|
|
371
|
+
if (labelSubMenu) {
|
|
372
|
+
return renderLabelSubMenuView();
|
|
373
|
+
}
|
|
374
|
+
const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
|
|
322
375
|
const isScrollable = totalItems > 10;
|
|
323
376
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ React.createElement(
|
|
324
377
|
"button",
|
|
@@ -330,7 +383,20 @@ function ContextMenu({
|
|
|
330
383
|
},
|
|
331
384
|
/* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "21", r: "2" })),
|
|
332
385
|
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
|
|
333
|
-
))),
|
|
386
|
+
))), labelGroupEntries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ React.createElement(
|
|
387
|
+
"button",
|
|
388
|
+
{
|
|
389
|
+
key: label,
|
|
390
|
+
onClick: () => {
|
|
391
|
+
setLabelSubMenu({ label, connections: conns });
|
|
392
|
+
},
|
|
393
|
+
className: baseButtonClass,
|
|
394
|
+
title: `Ver grupo: ${label}`
|
|
395
|
+
},
|
|
396
|
+
/* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
|
|
397
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, label),
|
|
398
|
+
/* @__PURE__ */ React.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
|
|
399
|
+
))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
|
|
334
400
|
if (group.isVersionGroup) {
|
|
335
401
|
return /* @__PURE__ */ React.createElement(
|
|
336
402
|
"button",
|
|
@@ -514,15 +580,18 @@ function XViewSidebar({
|
|
|
514
580
|
const [isFilterMenuOpen, setIsFilterMenuOpen] = useState2(false);
|
|
515
581
|
const [filterCategory, setFilterCategory] = useState2("Type");
|
|
516
582
|
const [filterValue, setFilterValue] = useState2("");
|
|
583
|
+
const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState2(false);
|
|
584
|
+
const [highlightedTypeIndex, setHighlightedTypeIndex] = useState2(-1);
|
|
517
585
|
const containerRef = useRef2(null);
|
|
518
586
|
const inputRef = useRef2(null);
|
|
519
587
|
const filterMenuRef = useRef2(null);
|
|
588
|
+
const typeDropdownRef = useRef2(null);
|
|
520
589
|
const ability = useMemo2(() => {
|
|
521
590
|
return defineAbilityFor(userRole);
|
|
522
591
|
}, [userRole]);
|
|
523
592
|
const contextLabel = useMemo2(() => {
|
|
524
593
|
if (!viewType || !viewName) return null;
|
|
525
|
-
const typeLower = viewType.toLowerCase();
|
|
594
|
+
const typeLower = String(viewType).toLowerCase();
|
|
526
595
|
if (typeLower === "database") {
|
|
527
596
|
return `Dataset: ${viewName}`;
|
|
528
597
|
} else if (typeLower === "view") {
|
|
@@ -532,7 +601,12 @@ function XViewSidebar({
|
|
|
532
601
|
}
|
|
533
602
|
return `${viewType}: ${viewName}`;
|
|
534
603
|
}, [viewType, viewName]);
|
|
535
|
-
const normalize = (str = "") =>
|
|
604
|
+
const normalize = (str = "") => {
|
|
605
|
+
if (str === void 0 || str === null) {
|
|
606
|
+
console.warn("XViewSidebar: normalize recebeu valor nulo/indefinido:", str);
|
|
607
|
+
}
|
|
608
|
+
return String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
|
|
609
|
+
};
|
|
536
610
|
const collator = useMemo2(
|
|
537
611
|
() => new Intl.Collator("pt-BR", { sensitivity: "base", numeric: true }),
|
|
538
612
|
[]
|
|
@@ -541,13 +615,30 @@ function XViewSidebar({
|
|
|
541
615
|
const typesSet = /* @__PURE__ */ new Set();
|
|
542
616
|
(dbNodes || []).forEach((node) => {
|
|
543
617
|
if (Array.isArray(node.type)) {
|
|
544
|
-
node.type.forEach((t) =>
|
|
545
|
-
|
|
618
|
+
node.type.forEach((t) => {
|
|
619
|
+
if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
|
|
620
|
+
});
|
|
621
|
+
} else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
|
|
546
622
|
typesSet.add(node.type);
|
|
547
623
|
}
|
|
548
624
|
});
|
|
549
625
|
return Array.from(typesSet).sort();
|
|
550
626
|
}, [dbNodes]);
|
|
627
|
+
const availableDatasets = useMemo2(() => {
|
|
628
|
+
const datasetsSet = /* @__PURE__ */ new Set();
|
|
629
|
+
(dbNodes || []).forEach((node) => {
|
|
630
|
+
if (node.dataset_name) {
|
|
631
|
+
datasetsSet.add(node.dataset_name);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
return Array.from(datasetsSet).sort();
|
|
635
|
+
}, [dbNodes]);
|
|
636
|
+
const filteredAutocompleteOptions = useMemo2(() => {
|
|
637
|
+
const source = filterCategory === "Dataset" ? availableDatasets : availableTypes;
|
|
638
|
+
if (!filterValue.trim()) return source;
|
|
639
|
+
const search = filterValue.toLowerCase().trim();
|
|
640
|
+
return source.filter((item) => item.toLowerCase().includes(search));
|
|
641
|
+
}, [availableTypes, availableDatasets, filterValue, filterCategory]);
|
|
551
642
|
const filteredAndSorted = useMemo2(() => {
|
|
552
643
|
const base = Array.isArray(dbNodes) ? dbNodes : [];
|
|
553
644
|
let pool = base;
|
|
@@ -567,6 +658,8 @@ function XViewSidebar({
|
|
|
567
658
|
return nodeTypes.some((t) => String(t).toLowerCase() === String(filter.value).toLowerCase());
|
|
568
659
|
} else if (filter.type === "Color") {
|
|
569
660
|
return String(node.color).toLowerCase() === String(filter.value).toLowerCase();
|
|
661
|
+
} else if (filter.type === "Dataset") {
|
|
662
|
+
return String(node.dataset_name || "").toLowerCase() === String(filter.value).toLowerCase();
|
|
570
663
|
}
|
|
571
664
|
return true;
|
|
572
665
|
});
|
|
@@ -582,18 +675,21 @@ function XViewSidebar({
|
|
|
582
675
|
inView.sort(byName);
|
|
583
676
|
const ordered = [...notInView, ...inView];
|
|
584
677
|
return ordered.slice(0, 200);
|
|
585
|
-
}, [dbNodes, query, isNodeInView, viewVersion, activeFilters]);
|
|
678
|
+
}, [dbNodes, query, isNodeInView, viewVersion, activeFilters, filterCategory]);
|
|
586
679
|
useEffect2(() => {
|
|
587
680
|
const handleClickOutside = (event) => {
|
|
588
681
|
if (filterMenuRef.current && !filterMenuRef.current.contains(event.target)) {
|
|
589
682
|
setIsFilterMenuOpen(false);
|
|
590
683
|
}
|
|
684
|
+
if (typeDropdownRef.current && !typeDropdownRef.current.contains(event.target)) {
|
|
685
|
+
setIsTypeDropdownOpen(false);
|
|
686
|
+
}
|
|
591
687
|
};
|
|
592
|
-
if (isFilterMenuOpen) {
|
|
688
|
+
if (isFilterMenuOpen || isTypeDropdownOpen) {
|
|
593
689
|
document.addEventListener("mousedown", handleClickOutside);
|
|
594
690
|
}
|
|
595
691
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
596
|
-
}, [isFilterMenuOpen]);
|
|
692
|
+
}, [isFilterMenuOpen, isTypeDropdownOpen]);
|
|
597
693
|
useEffect2(() => {
|
|
598
694
|
if (!query) setSelectedNodeId(null);
|
|
599
695
|
}, [query]);
|
|
@@ -647,6 +743,35 @@ function XViewSidebar({
|
|
|
647
743
|
const handleRemoveFilter = (index) => {
|
|
648
744
|
setActiveFilters((prev) => prev.filter((_, i) => i !== index));
|
|
649
745
|
};
|
|
746
|
+
const handleTypeKeyDown = (e) => {
|
|
747
|
+
if (!isTypeDropdownOpen) {
|
|
748
|
+
setIsTypeDropdownOpen(true);
|
|
749
|
+
}
|
|
750
|
+
if (e.key === "ArrowDown") {
|
|
751
|
+
e.preventDefault();
|
|
752
|
+
setHighlightedTypeIndex(
|
|
753
|
+
(prev) => prev < filteredAutocompleteOptions.length - 1 ? prev + 1 : prev
|
|
754
|
+
);
|
|
755
|
+
} else if (e.key === "ArrowUp") {
|
|
756
|
+
e.preventDefault();
|
|
757
|
+
setHighlightedTypeIndex((prev) => prev > 0 ? prev - 1 : 0);
|
|
758
|
+
} else if (e.key === "Enter") {
|
|
759
|
+
e.preventDefault();
|
|
760
|
+
if (highlightedTypeIndex >= 0 && filteredAutocompleteOptions[highlightedTypeIndex]) {
|
|
761
|
+
setFilterValue(filteredAutocompleteOptions[highlightedTypeIndex]);
|
|
762
|
+
setIsTypeDropdownOpen(false);
|
|
763
|
+
setHighlightedTypeIndex(-1);
|
|
764
|
+
} else {
|
|
765
|
+
handleAddFilter();
|
|
766
|
+
}
|
|
767
|
+
} else if (e.key === "Escape") {
|
|
768
|
+
e.preventDefault();
|
|
769
|
+
setIsTypeDropdownOpen(false);
|
|
770
|
+
setHighlightedTypeIndex(-1);
|
|
771
|
+
} else if (e.key === "Tab") {
|
|
772
|
+
setIsTypeDropdownOpen(false);
|
|
773
|
+
}
|
|
774
|
+
};
|
|
650
775
|
const ToggleButton = /* @__PURE__ */ React2.createElement(
|
|
651
776
|
"button",
|
|
652
777
|
{
|
|
@@ -666,7 +791,7 @@ function XViewSidebar({
|
|
|
666
791
|
"div",
|
|
667
792
|
{
|
|
668
793
|
ref: containerRef,
|
|
669
|
-
className: "ui-overlay fixed left-0 top-0 h-
|
|
794
|
+
className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
|
|
670
795
|
onPointerDown: swallow,
|
|
671
796
|
onPointerMove: swallow,
|
|
672
797
|
onPointerUp: swallow,
|
|
@@ -675,7 +800,7 @@ function XViewSidebar({
|
|
|
675
800
|
onContextMenu: swallow,
|
|
676
801
|
onDoubleClick: swallow
|
|
677
802
|
},
|
|
678
|
-
/* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
|
|
803
|
+
/* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
|
|
679
804
|
"button",
|
|
680
805
|
{
|
|
681
806
|
className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
@@ -732,7 +857,7 @@ function XViewSidebar({
|
|
|
732
857
|
autoComplete: "off"
|
|
733
858
|
}
|
|
734
859
|
)
|
|
735
|
-
), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[
|
|
860
|
+
), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
|
|
736
861
|
const inView = isNodeInView(n.id);
|
|
737
862
|
const active = selectedNodeId === n.id;
|
|
738
863
|
const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
|
|
@@ -766,7 +891,7 @@ function XViewSidebar({
|
|
|
766
891
|
},
|
|
767
892
|
/* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })),
|
|
768
893
|
"Filters"
|
|
769
|
-
), isFilterMenuOpen && /* @__PURE__ */ React2.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ React2.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color"].map((opt) => /* @__PURE__ */ React2.createElement(
|
|
894
|
+
), isFilterMenuOpen && /* @__PURE__ */ React2.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ React2.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ React2.createElement(
|
|
770
895
|
"button",
|
|
771
896
|
{
|
|
772
897
|
key: opt,
|
|
@@ -777,18 +902,36 @@ function XViewSidebar({
|
|
|
777
902
|
className: `flex-1 text-xs py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white shadow-sm" : "text-slate-400 hover:text-slate-200"}`
|
|
778
903
|
},
|
|
779
904
|
opt
|
|
780
|
-
)))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" ? /* @__PURE__ */ React2.createElement("div", { className: "relative" }, /* @__PURE__ */ React2.createElement(
|
|
905
|
+
)))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" || filterCategory === "Dataset" ? /* @__PURE__ */ React2.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ React2.createElement(
|
|
781
906
|
"input",
|
|
782
907
|
{
|
|
783
|
-
list: "typeOptions",
|
|
784
908
|
type: "text",
|
|
785
909
|
value: filterValue,
|
|
786
|
-
onChange: (e) =>
|
|
787
|
-
|
|
910
|
+
onChange: (e) => {
|
|
911
|
+
setFilterValue(e.target.value);
|
|
912
|
+
if (!isTypeDropdownOpen) setIsTypeDropdownOpen(true);
|
|
913
|
+
setHighlightedTypeIndex(-1);
|
|
914
|
+
},
|
|
915
|
+
onFocus: () => setIsTypeDropdownOpen(true),
|
|
916
|
+
onKeyDown: handleTypeKeyDown,
|
|
917
|
+
placeholder: filterCategory === "Type" ? "Ex: Concept" : "Ex: Dataset 1",
|
|
788
918
|
className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white",
|
|
789
919
|
autoFocus: true
|
|
790
920
|
}
|
|
791
|
-
),
|
|
921
|
+
), isTypeDropdownOpen && filteredAutocompleteOptions.length > 0 && /* @__PURE__ */ React2.createElement("div", { className: "absolute z-[60] left-0 right-0 mt-1 max-h-48 overflow-y-auto bg-slate-900/95 backdrop-blur-xl border border-white/10 rounded-md shadow-2xl custom-scrollbar" }, filteredAutocompleteOptions.map((t, idx) => /* @__PURE__ */ React2.createElement(
|
|
922
|
+
"div",
|
|
923
|
+
{
|
|
924
|
+
key: t,
|
|
925
|
+
className: `px-3 py-2 text-xs cursor-pointer transition-colors ${idx === highlightedTypeIndex ? "bg-indigo-600 text-white" : "text-slate-300 hover:bg-white/10"}`,
|
|
926
|
+
onMouseDown: (e) => {
|
|
927
|
+
e.preventDefault();
|
|
928
|
+
setFilterValue(t);
|
|
929
|
+
setIsTypeDropdownOpen(false);
|
|
930
|
+
setHighlightedTypeIndex(-1);
|
|
931
|
+
}
|
|
932
|
+
},
|
|
933
|
+
t
|
|
934
|
+
)))) : /* @__PURE__ */ React2.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ React2.createElement(
|
|
792
935
|
"div",
|
|
793
936
|
{
|
|
794
937
|
className: "absolute left-2 w-3 h-3 rounded-full border border-white/20 shadow-sm",
|
|
@@ -2776,18 +2919,27 @@ function createNewCustomProperty(existingProps = []) {
|
|
|
2776
2919
|
};
|
|
2777
2920
|
}
|
|
2778
2921
|
var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
|
|
2779
|
-
const match = refString.match(
|
|
2922
|
+
const match = refString.match(
|
|
2923
|
+
/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
|
|
2924
|
+
);
|
|
2780
2925
|
if (!match) return null;
|
|
2781
2926
|
const [_, type, itemId, sectionId] = match;
|
|
2782
2927
|
let sourceItem = null;
|
|
2783
2928
|
if (type === "node") {
|
|
2784
2929
|
sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
|
|
2785
2930
|
} else {
|
|
2786
|
-
sourceItem = availableAncestries.find(
|
|
2931
|
+
sourceItem = availableAncestries.find(
|
|
2932
|
+
(a) => String(a.ancestry_id) === String(itemId)
|
|
2933
|
+
);
|
|
2787
2934
|
}
|
|
2788
2935
|
if (!sourceItem) return null;
|
|
2789
|
-
const sections = parseDescriptionSections(
|
|
2790
|
-
|
|
2936
|
+
const sections = parseDescriptionSections(
|
|
2937
|
+
sourceItem.description,
|
|
2938
|
+
sourceItem.description_sections
|
|
2939
|
+
);
|
|
2940
|
+
const targetSection = sections.find(
|
|
2941
|
+
(s) => String(s.id) === String(sectionId)
|
|
2942
|
+
);
|
|
2791
2943
|
if (!targetSection) return null;
|
|
2792
2944
|
return {
|
|
2793
2945
|
content: targetSection.content,
|
|
@@ -2801,21 +2953,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
|
|
|
2801
2953
|
let text = rawText;
|
|
2802
2954
|
const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
|
|
2803
2955
|
const allAncestries = ancestryData || [];
|
|
2804
|
-
text = text.replace(
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2956
|
+
text = text.replace(
|
|
2957
|
+
/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
|
|
2958
|
+
(match, type, itemId, sectionId) => {
|
|
2959
|
+
const resolved = resolveDescriptionReference(
|
|
2960
|
+
match,
|
|
2961
|
+
allNodes,
|
|
2962
|
+
allAncestries
|
|
2963
|
+
);
|
|
2964
|
+
if (resolved && !resolved.error) {
|
|
2965
|
+
return resolved.content;
|
|
2966
|
+
}
|
|
2967
|
+
return "[Refer\xEAncia indispon\xEDvel]";
|
|
2808
2968
|
}
|
|
2809
|
-
|
|
2810
|
-
});
|
|
2969
|
+
);
|
|
2811
2970
|
text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
|
|
2812
|
-
text = text.replace(
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2971
|
+
text = text.replace(
|
|
2972
|
+
/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
|
|
2973
|
+
(match, nodeId) => {
|
|
2974
|
+
const node = allNodes.find((n) => String(n.id) === String(nodeId));
|
|
2975
|
+
return node ? `@${node.name}` : `@Men\xE7\xE3o`;
|
|
2976
|
+
}
|
|
2977
|
+
);
|
|
2978
|
+
text = text.replace(
|
|
2979
|
+
/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
|
|
2980
|
+
(match, url, name) => {
|
|
2981
|
+
return name ? `[Imagem: ${name}]` : `[Imagem]`;
|
|
2982
|
+
}
|
|
2983
|
+
);
|
|
2819
2984
|
text = text.replace(/^#+\s*/gm, "");
|
|
2820
2985
|
text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
|
|
2821
2986
|
text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
|
|
@@ -2823,7 +2988,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
|
|
|
2823
2988
|
return text.trim();
|
|
2824
2989
|
}
|
|
2825
2990
|
function generateTooltipHtml(data, parentData, ancestryData) {
|
|
2826
|
-
const ignoredKeys = [
|
|
2991
|
+
const ignoredKeys = [
|
|
2992
|
+
"id",
|
|
2993
|
+
"name",
|
|
2994
|
+
"type",
|
|
2995
|
+
"color",
|
|
2996
|
+
"_baseEmissiveIntensity",
|
|
2997
|
+
"description"
|
|
2998
|
+
];
|
|
2827
2999
|
const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
|
|
2828
3000
|
const extras = customKeys.length;
|
|
2829
3001
|
let typeDisplay = "Node";
|
|
@@ -2838,7 +3010,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
|
|
|
2838
3010
|
<div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
|
|
2839
3011
|
<div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
|
|
2840
3012
|
if (data.description) {
|
|
2841
|
-
const cleanDesc = formatDescriptionForTooltip(
|
|
3013
|
+
const cleanDesc = formatDescriptionForTooltip(
|
|
3014
|
+
data.description,
|
|
3015
|
+
parentData,
|
|
3016
|
+
ancestryData
|
|
3017
|
+
);
|
|
2842
3018
|
if (cleanDesc) {
|
|
2843
3019
|
html += `<div style="
|
|
2844
3020
|
margin-top: 6px;
|
|
@@ -2876,7 +3052,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
|
|
|
2876
3052
|
html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
|
|
2877
3053
|
}
|
|
2878
3054
|
if (hasDescription) {
|
|
2879
|
-
const cleanDesc = formatDescriptionForTooltip(
|
|
3055
|
+
const cleanDesc = formatDescriptionForTooltip(
|
|
3056
|
+
data.description,
|
|
3057
|
+
parentData,
|
|
3058
|
+
ancestryData
|
|
3059
|
+
);
|
|
2880
3060
|
if (cleanDesc) {
|
|
2881
3061
|
html += `<div style="
|
|
2882
3062
|
display: -webkit-box;
|
|
@@ -2958,7 +3138,8 @@ var IGNORED_KEYS = [
|
|
|
2958
3138
|
"isAddingAbstractionNodes",
|
|
2959
3139
|
"status",
|
|
2960
3140
|
"is_quest",
|
|
2961
|
-
"raw_title"
|
|
3141
|
+
"raw_title",
|
|
3142
|
+
"assignee_id"
|
|
2962
3143
|
];
|
|
2963
3144
|
function extractCustomPropsFromNode(node) {
|
|
2964
3145
|
const customPropTypes = node._customPropTypes || {};
|
|
@@ -2968,16 +3149,40 @@ function extractCustomPropsFromNode(node) {
|
|
|
2968
3149
|
if (t === "date") {
|
|
2969
3150
|
if (val && typeof val === "object") {
|
|
2970
3151
|
if ("date_interval" in val) {
|
|
2971
|
-
return {
|
|
3152
|
+
return {
|
|
3153
|
+
id: v4_default(),
|
|
3154
|
+
key,
|
|
3155
|
+
type: "date",
|
|
3156
|
+
value: {
|
|
3157
|
+
type: "Date Interval",
|
|
3158
|
+
start: val.date_interval.start || "",
|
|
3159
|
+
end: val.date_interval.end || ""
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
2972
3162
|
}
|
|
2973
3163
|
if ("year" in val) {
|
|
2974
|
-
return {
|
|
3164
|
+
return {
|
|
3165
|
+
id: v4_default(),
|
|
3166
|
+
key,
|
|
3167
|
+
type: "date",
|
|
3168
|
+
value: { type: "Ano", value: val.year || "" }
|
|
3169
|
+
};
|
|
2975
3170
|
}
|
|
2976
3171
|
if ("date" in val) {
|
|
2977
|
-
return {
|
|
3172
|
+
return {
|
|
3173
|
+
id: v4_default(),
|
|
3174
|
+
key,
|
|
3175
|
+
type: "date",
|
|
3176
|
+
value: { type: "Data", value: val.date || "" }
|
|
3177
|
+
};
|
|
2978
3178
|
}
|
|
2979
3179
|
}
|
|
2980
|
-
return {
|
|
3180
|
+
return {
|
|
3181
|
+
id: v4_default(),
|
|
3182
|
+
key,
|
|
3183
|
+
type: "date",
|
|
3184
|
+
value: { type: "Data", value: "" }
|
|
3185
|
+
};
|
|
2981
3186
|
}
|
|
2982
3187
|
if (t === "list" || t === "links" || t === "images" || t === "documents") {
|
|
2983
3188
|
const safeList = (val || []).map((item) => ({
|
|
@@ -2987,7 +3192,12 @@ function extractCustomPropsFromNode(node) {
|
|
|
2987
3192
|
}));
|
|
2988
3193
|
return { id: v4_default(), key, type: t, value: safeList };
|
|
2989
3194
|
}
|
|
2990
|
-
return {
|
|
3195
|
+
return {
|
|
3196
|
+
id: v4_default(),
|
|
3197
|
+
key,
|
|
3198
|
+
type: t,
|
|
3199
|
+
value: t === "number" ? Number(val) || 0 : String(val ?? "")
|
|
3200
|
+
};
|
|
2991
3201
|
});
|
|
2992
3202
|
}
|
|
2993
3203
|
function toObjectFromCustomProps(customProps) {
|
|
@@ -3002,7 +3212,12 @@ function toObjectFromCustomProps(customProps) {
|
|
|
3002
3212
|
const { type: uiDateType, ...dateValues } = p.value || {};
|
|
3003
3213
|
if (uiDateType) {
|
|
3004
3214
|
if (uiDateType === "Date Interval") {
|
|
3005
|
-
out[key] = {
|
|
3215
|
+
out[key] = {
|
|
3216
|
+
date_interval: {
|
|
3217
|
+
start: dateValues.start || "",
|
|
3218
|
+
end: dateValues.end || ""
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
3006
3221
|
} else if (uiDateType === "Ano") {
|
|
3007
3222
|
out[key] = { year: dateValues.value || "" };
|
|
3008
3223
|
} else {
|
|
@@ -3038,10 +3253,20 @@ function createTextSprite(text, fontSize = 64) {
|
|
|
3038
3253
|
context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
|
|
3039
3254
|
const texture = new THREE2.CanvasTexture(canvas);
|
|
3040
3255
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
3041
|
-
const spriteMaterial = new THREE2.SpriteMaterial({
|
|
3256
|
+
const spriteMaterial = new THREE2.SpriteMaterial({
|
|
3257
|
+
map: texture,
|
|
3258
|
+
sizeAttenuation: true,
|
|
3259
|
+
depthWrite: false,
|
|
3260
|
+
transparent: true,
|
|
3261
|
+
toneMapped: false
|
|
3262
|
+
});
|
|
3042
3263
|
const sprite = new THREE2.Sprite(spriteMaterial);
|
|
3043
3264
|
const scaleFactor = 0.05;
|
|
3044
|
-
sprite.scale.set(
|
|
3265
|
+
sprite.scale.set(
|
|
3266
|
+
canvas.width * scaleFactor,
|
|
3267
|
+
canvas.height * scaleFactor,
|
|
3268
|
+
1
|
|
3269
|
+
);
|
|
3045
3270
|
sprite.layers.set(DEFAULT_LAYER);
|
|
3046
3271
|
return sprite;
|
|
3047
3272
|
}
|
|
@@ -3057,7 +3282,14 @@ function makeGlowTexture() {
|
|
|
3057
3282
|
const canvas = document.createElement("canvas");
|
|
3058
3283
|
canvas.width = canvas.height = size;
|
|
3059
3284
|
const ctx = canvas.getContext("2d");
|
|
3060
|
-
const grd = ctx.createRadialGradient(
|
|
3285
|
+
const grd = ctx.createRadialGradient(
|
|
3286
|
+
size / 2,
|
|
3287
|
+
size / 2,
|
|
3288
|
+
0,
|
|
3289
|
+
size / 2,
|
|
3290
|
+
size / 2,
|
|
3291
|
+
size / 2
|
|
3292
|
+
);
|
|
3061
3293
|
grd.addColorStop(0, "rgba(255,255,255,1)");
|
|
3062
3294
|
grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
|
|
3063
3295
|
grd.addColorStop(1, "rgba(255,255,255,0)");
|
|
@@ -3073,8 +3305,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
|
|
|
3073
3305
|
let midBoost = 1 - Math.abs(L - 0.5) * 2;
|
|
3074
3306
|
midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
|
|
3075
3307
|
const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
|
|
3076
|
-
const auraColor = new THREE2.Color(hexColor).lerp(
|
|
3077
|
-
|
|
3308
|
+
const auraColor = new THREE2.Color(hexColor).lerp(
|
|
3309
|
+
new THREE2.Color("#ffffff"),
|
|
3310
|
+
0.25
|
|
3311
|
+
);
|
|
3312
|
+
const auraMat = new THREE2.SpriteMaterial({
|
|
3313
|
+
map: glowTexture,
|
|
3314
|
+
color: auraColor,
|
|
3315
|
+
opacity: auraOpacity,
|
|
3316
|
+
blending: THREE2.AdditiveBlending,
|
|
3317
|
+
transparent: true,
|
|
3318
|
+
depthWrite: false,
|
|
3319
|
+
toneMapped: false
|
|
3320
|
+
});
|
|
3078
3321
|
const aura = new THREE2.Sprite(auraMat);
|
|
3079
3322
|
aura.scale.setScalar(auraScale);
|
|
3080
3323
|
aura.name = "aura";
|
|
@@ -3107,7 +3350,17 @@ function calculateNodePositions(nodes) {
|
|
|
3107
3350
|
}
|
|
3108
3351
|
return positions;
|
|
3109
3352
|
}
|
|
3110
|
-
function updateTooltip({
|
|
3353
|
+
function updateTooltip({
|
|
3354
|
+
tooltipEl,
|
|
3355
|
+
hoveredNode,
|
|
3356
|
+
hoveredLink,
|
|
3357
|
+
camera,
|
|
3358
|
+
mountEl,
|
|
3359
|
+
isSceneBusy,
|
|
3360
|
+
parentData,
|
|
3361
|
+
ancestryData
|
|
3362
|
+
}) {
|
|
3363
|
+
var _a, _b;
|
|
3111
3364
|
if (!tooltipEl || !camera || !mountEl) return;
|
|
3112
3365
|
let content = "";
|
|
3113
3366
|
let positionTarget = null;
|
|
@@ -3117,20 +3370,35 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3117
3370
|
currentId = `node_${hoveredNode.userData.id}`;
|
|
3118
3371
|
positionTarget = hoveredNode;
|
|
3119
3372
|
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3120
|
-
content = generateTooltipHtml(
|
|
3373
|
+
content = generateTooltipHtml(
|
|
3374
|
+
hoveredNode.userData,
|
|
3375
|
+
parentData,
|
|
3376
|
+
ancestryData
|
|
3377
|
+
);
|
|
3121
3378
|
}
|
|
3122
3379
|
} else if (hoveredLink && !isSceneBusy) {
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3380
|
+
const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
|
|
3381
|
+
const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
|
|
3382
|
+
if (hasContent) {
|
|
3383
|
+
currentId = `link_${hoveredLink.userData.id}`;
|
|
3384
|
+
if (hoveredLink.userData.isCurved) {
|
|
3385
|
+
const positions = hoveredLink.geometry.attributes.position.array;
|
|
3386
|
+
const midIndex = Math.floor(positions.length / 2 / 3) * 3;
|
|
3387
|
+
positionTarget = new THREE2.Vector3(
|
|
3388
|
+
positions[midIndex],
|
|
3389
|
+
positions[midIndex + 1],
|
|
3390
|
+
positions[midIndex + 2]
|
|
3391
|
+
);
|
|
3392
|
+
} else {
|
|
3393
|
+
positionTarget = new THREE2.Vector3().addVectors(
|
|
3394
|
+
hoveredLink.userData.sourceNode.position,
|
|
3395
|
+
hoveredLink.userData.targetNode.position
|
|
3396
|
+
).multiplyScalar(0.5);
|
|
3397
|
+
}
|
|
3398
|
+
isLink = true;
|
|
3399
|
+
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3400
|
+
content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
|
|
3401
|
+
}
|
|
3134
3402
|
}
|
|
3135
3403
|
}
|
|
3136
3404
|
if (positionTarget) {
|
|
@@ -3154,9 +3422,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3154
3422
|
const tooltipRect = tooltipEl.getBoundingClientRect();
|
|
3155
3423
|
const offsetX = 20;
|
|
3156
3424
|
const offsetY = 20;
|
|
3157
|
-
if (x + tooltipRect.width + offsetX > clientWidth)
|
|
3425
|
+
if (x + tooltipRect.width + offsetX > clientWidth)
|
|
3426
|
+
x = x - tooltipRect.width - offsetX;
|
|
3158
3427
|
else x = x + offsetX;
|
|
3159
|
-
if (y + tooltipRect.height + offsetY > clientHeight)
|
|
3428
|
+
if (y + tooltipRect.height + offsetY > clientHeight)
|
|
3429
|
+
y = y - tooltipRect.height - offsetY;
|
|
3160
3430
|
else y = y + offsetY;
|
|
3161
3431
|
tooltipEl.style.display = "block";
|
|
3162
3432
|
tooltipEl.style.left = `${x}px`;
|
|
@@ -3190,7 +3460,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
|
|
|
3190
3460
|
const content = parts[i + 2] || "";
|
|
3191
3461
|
let finalUuid = null;
|
|
3192
3462
|
if (suffix) {
|
|
3193
|
-
const existingMatch = existingSections.find(
|
|
3463
|
+
const existingMatch = existingSections.find(
|
|
3464
|
+
(s) => s.id && s.id.includes(suffix)
|
|
3465
|
+
);
|
|
3194
3466
|
if (existingMatch) {
|
|
3195
3467
|
finalUuid = existingMatch.id;
|
|
3196
3468
|
} else {
|
|
@@ -3281,28 +3553,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
|
|
|
3281
3553
|
function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
|
|
3282
3554
|
const [width, setWidth] = useState3(initialWidth);
|
|
3283
3555
|
const [isResizing, setIsResizing] = useState3(false);
|
|
3284
|
-
const handlePointerDown = useCallback(
|
|
3285
|
-
e
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
const
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3556
|
+
const handlePointerDown = useCallback(
|
|
3557
|
+
(e) => {
|
|
3558
|
+
e.preventDefault();
|
|
3559
|
+
e.stopPropagation();
|
|
3560
|
+
setIsResizing(true);
|
|
3561
|
+
const startX = e.clientX;
|
|
3562
|
+
const startWidth = width;
|
|
3563
|
+
const originalUserSelect = document.body.style.userSelect;
|
|
3564
|
+
document.body.style.userSelect = "none";
|
|
3565
|
+
const handlePointerMove = (moveEvent) => {
|
|
3566
|
+
const deltaX = startX - moveEvent.clientX;
|
|
3567
|
+
const newWidth = Math.min(
|
|
3568
|
+
Math.max(startWidth + deltaX, minWidth),
|
|
3569
|
+
maxWidth
|
|
3570
|
+
);
|
|
3571
|
+
setWidth(newWidth);
|
|
3572
|
+
};
|
|
3573
|
+
const handlePointerUp = () => {
|
|
3574
|
+
setIsResizing(false);
|
|
3575
|
+
document.body.style.userSelect = originalUserSelect;
|
|
3576
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
3577
|
+
document.removeEventListener("pointerup", handlePointerUp);
|
|
3578
|
+
};
|
|
3579
|
+
document.addEventListener("pointermove", handlePointerMove);
|
|
3580
|
+
document.addEventListener("pointerup", handlePointerUp);
|
|
3581
|
+
},
|
|
3582
|
+
[width, minWidth, maxWidth]
|
|
3583
|
+
);
|
|
3306
3584
|
return { width, isResizing, handlePointerDown, setWidth };
|
|
3307
3585
|
}
|
|
3308
3586
|
|
|
@@ -3439,9 +3717,9 @@ function CustomPropertyDisplay({
|
|
|
3439
3717
|
};
|
|
3440
3718
|
const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
|
|
3441
3719
|
const handleListItemChange = (j, f, v) => setTempProp((p) => {
|
|
3442
|
-
const
|
|
3443
|
-
|
|
3444
|
-
return { ...p, value:
|
|
3720
|
+
const newValue2 = [...p.value];
|
|
3721
|
+
newValue2[j] = { ...newValue2[j], [f]: v };
|
|
3722
|
+
return { ...p, value: newValue2 };
|
|
3445
3723
|
});
|
|
3446
3724
|
const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
|
|
3447
3725
|
const renderEditView = () => {
|
|
@@ -3495,14 +3773,14 @@ function CustomPropertyDisplay({
|
|
|
3495
3773
|
const inputClass = `${baseInput} ${noSpinnerClass}`;
|
|
3496
3774
|
const handleDateTypeChange = (newDateType) => {
|
|
3497
3775
|
var _a3, _b2, _c2;
|
|
3498
|
-
let
|
|
3776
|
+
let newValue2 = { type: newDateType };
|
|
3499
3777
|
if (newDateType === "Date Interval") {
|
|
3500
|
-
|
|
3501
|
-
|
|
3778
|
+
newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
|
|
3779
|
+
newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
|
|
3502
3780
|
} else {
|
|
3503
|
-
|
|
3781
|
+
newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
|
|
3504
3782
|
}
|
|
3505
|
-
handlePropChange("value",
|
|
3783
|
+
handlePropChange("value", newValue2);
|
|
3506
3784
|
};
|
|
3507
3785
|
const handleDateValueChange = (dateField, dateValue) => {
|
|
3508
3786
|
handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
|
|
@@ -4350,7 +4628,7 @@ ${space}${bullet} `);
|
|
|
4350
4628
|
}
|
|
4351
4629
|
),
|
|
4352
4630
|
/* @__PURE__ */ React5.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
|
|
4353
|
-
/* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
|
|
4631
|
+
/* @__PURE__ */ React5.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React5.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ React5.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ React5.createElement("button", { onClick: handleClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
|
|
4354
4632
|
/* @__PURE__ */ React5.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ React5.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ React5.createElement(
|
|
4355
4633
|
"button",
|
|
4356
4634
|
{
|
|
@@ -5479,7 +5757,7 @@ function AncestryRelationshipPanel({
|
|
|
5479
5757
|
onImageClick: handleImageClickFromText,
|
|
5480
5758
|
onSaveDescription: handleSaveDescriptionInline
|
|
5481
5759
|
}
|
|
5482
|
-
) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div",
|
|
5760
|
+
) : /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React8.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React8.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React8.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React8.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ React8.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ React8.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React8.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ React8.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React8.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ React8.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React8.createElement(
|
|
5483
5761
|
DescriptionDisplay,
|
|
5484
5762
|
{
|
|
5485
5763
|
description,
|
|
@@ -5801,6 +6079,7 @@ function CreateAncestryPanel({
|
|
|
5801
6079
|
} = ancestryMode;
|
|
5802
6080
|
const [isSaving, setIsSaving] = useState11(false);
|
|
5803
6081
|
const [isLinkCopied, setIsLinkCopied] = useState11(false);
|
|
6082
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState11(false);
|
|
5804
6083
|
const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = useState11(false);
|
|
5805
6084
|
const handleCopyLink = (e) => {
|
|
5806
6085
|
e.stopPropagation();
|
|
@@ -5868,12 +6147,14 @@ function CreateAncestryPanel({
|
|
|
5868
6147
|
};
|
|
5869
6148
|
const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
|
|
5870
6149
|
setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
|
|
6150
|
+
setHasUnsavedChanges(true);
|
|
5871
6151
|
};
|
|
5872
6152
|
const handleToggleAddMode = (isAbstraction = false) => {
|
|
5873
6153
|
if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
|
|
5874
6154
|
setTargetRenderNodeId(null);
|
|
5875
6155
|
}
|
|
5876
6156
|
setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
|
|
6157
|
+
setHasUnsavedChanges(true);
|
|
5877
6158
|
};
|
|
5878
6159
|
const handleRemoveNode = useCallback2((pathToRemove, isAbstraction = false) => {
|
|
5879
6160
|
if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
|
|
@@ -5891,6 +6172,7 @@ function CreateAncestryPanel({
|
|
|
5891
6172
|
const indexToRemove = pathToRemove[pathToRemove.length - 1];
|
|
5892
6173
|
if (currentParent.children && currentParent.children.length > indexToRemove) {
|
|
5893
6174
|
currentParent.children.splice(indexToRemove, 1);
|
|
6175
|
+
setHasUnsavedChanges(true);
|
|
5894
6176
|
}
|
|
5895
6177
|
return { ...prev, [treeKey]: newTree };
|
|
5896
6178
|
});
|
|
@@ -5949,6 +6231,7 @@ function CreateAncestryPanel({
|
|
|
5949
6231
|
updateGlobalTree(rootTreeClone);
|
|
5950
6232
|
}
|
|
5951
6233
|
setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
|
|
6234
|
+
setHasUnsavedChanges(true);
|
|
5952
6235
|
} else {
|
|
5953
6236
|
alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
|
|
5954
6237
|
}
|
|
@@ -6021,6 +6304,7 @@ function CreateAncestryPanel({
|
|
|
6021
6304
|
const handleAddProp = () => {
|
|
6022
6305
|
const newProp = createNewCustomProperty(customProps);
|
|
6023
6306
|
setCustomProps((p) => [...p, newProp]);
|
|
6307
|
+
setHasUnsavedChanges(true);
|
|
6024
6308
|
setTimeout(() => {
|
|
6025
6309
|
var _a;
|
|
6026
6310
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -6029,11 +6313,13 @@ function CreateAncestryPanel({
|
|
|
6029
6313
|
const handleRemoveProp = (i) => {
|
|
6030
6314
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
6031
6315
|
setCustomProps(newProps);
|
|
6316
|
+
setHasUnsavedChanges(true);
|
|
6032
6317
|
};
|
|
6033
6318
|
const handleUpdateProp = (index, updatedProp) => {
|
|
6034
6319
|
const newProps = [...customProps];
|
|
6035
6320
|
newProps[index] = updatedProp;
|
|
6036
6321
|
setCustomProps(newProps);
|
|
6322
|
+
setHasUnsavedChanges(true);
|
|
6037
6323
|
};
|
|
6038
6324
|
const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
|
|
6039
6325
|
useEffect10(() => {
|
|
@@ -6143,6 +6429,7 @@ function CreateAncestryPanel({
|
|
|
6143
6429
|
updateGlobalTree(rootTreeClone);
|
|
6144
6430
|
setBranchStack([...branchStack]);
|
|
6145
6431
|
setIsPickerOpen(false);
|
|
6432
|
+
setHasUnsavedChanges(true);
|
|
6146
6433
|
try {
|
|
6147
6434
|
setIsSaving(true);
|
|
6148
6435
|
const rootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6156,6 +6443,7 @@ function CreateAncestryPanel({
|
|
|
6156
6443
|
rootExtras
|
|
6157
6444
|
);
|
|
6158
6445
|
setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
|
|
6446
|
+
setHasUnsavedChanges(false);
|
|
6159
6447
|
if (onRenderFullAncestry) {
|
|
6160
6448
|
const fullTreePayload = {
|
|
6161
6449
|
ancestry_id: ancestryMode.currentAncestryId || "temp_root",
|
|
@@ -6198,6 +6486,7 @@ function CreateAncestryPanel({
|
|
|
6198
6486
|
if (branchIndex !== -1) {
|
|
6199
6487
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6200
6488
|
updateGlobalTree(rootTreeClone);
|
|
6489
|
+
setHasUnsavedChanges(true);
|
|
6201
6490
|
try {
|
|
6202
6491
|
setIsSaving(true);
|
|
6203
6492
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6219,6 +6508,7 @@ function CreateAncestryPanel({
|
|
|
6219
6508
|
isPrivate,
|
|
6220
6509
|
ancestryMode.abstraction_tree
|
|
6221
6510
|
));
|
|
6511
|
+
setHasUnsavedChanges(false);
|
|
6222
6512
|
if (onClearAncestryVisuals) {
|
|
6223
6513
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6224
6514
|
}
|
|
@@ -6251,6 +6541,7 @@ function CreateAncestryPanel({
|
|
|
6251
6541
|
if (branchIndex !== -1) {
|
|
6252
6542
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6253
6543
|
updateGlobalTree(rootTreeClone);
|
|
6544
|
+
setHasUnsavedChanges(true);
|
|
6254
6545
|
try {
|
|
6255
6546
|
setIsSaving(true);
|
|
6256
6547
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6272,6 +6563,7 @@ function CreateAncestryPanel({
|
|
|
6272
6563
|
isPrivate,
|
|
6273
6564
|
ancestryMode.abstraction_tree
|
|
6274
6565
|
));
|
|
6566
|
+
setHasUnsavedChanges(false);
|
|
6275
6567
|
if (onClearAncestryVisuals) {
|
|
6276
6568
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6277
6569
|
}
|
|
@@ -6533,6 +6825,7 @@ function CreateAncestryPanel({
|
|
|
6533
6825
|
}
|
|
6534
6826
|
setBranchStack(parentStack);
|
|
6535
6827
|
setTargetScrollSectionId(targetFocusId);
|
|
6828
|
+
setHasUnsavedChanges(true);
|
|
6536
6829
|
if (onRenderFullAncestry) {
|
|
6537
6830
|
const parentStack2 = currentStack;
|
|
6538
6831
|
const rotation = parentStack2.reduce((acc, step) => {
|
|
@@ -6594,7 +6887,6 @@ function CreateAncestryPanel({
|
|
|
6594
6887
|
direction,
|
|
6595
6888
|
tree: {
|
|
6596
6889
|
node: nodeData,
|
|
6597
|
-
node_id: nodeId,
|
|
6598
6890
|
children: [],
|
|
6599
6891
|
relationship: {}
|
|
6600
6892
|
}
|
|
@@ -6616,6 +6908,7 @@ function CreateAncestryPanel({
|
|
|
6616
6908
|
savedMaxIndex: parentIndexToSave,
|
|
6617
6909
|
entryDirection: direction
|
|
6618
6910
|
}]);
|
|
6911
|
+
setHasUnsavedChanges(true);
|
|
6619
6912
|
if (branch && branch.tree && onRenderFullAncestry) {
|
|
6620
6913
|
const branchAncestryObj = {
|
|
6621
6914
|
ancestry_id: branch.id,
|
|
@@ -6666,6 +6959,10 @@ function CreateAncestryPanel({
|
|
|
6666
6959
|
const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
|
|
6667
6960
|
const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
|
|
6668
6961
|
const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
6962
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
6963
|
+
onClose();
|
|
6964
|
+
return;
|
|
6965
|
+
}
|
|
6669
6966
|
if (!currentInputName.trim()) {
|
|
6670
6967
|
alert("O nome n\xE3o pode estar vazio.");
|
|
6671
6968
|
return;
|
|
@@ -6673,11 +6970,7 @@ function CreateAncestryPanel({
|
|
|
6673
6970
|
setIsSaving(true);
|
|
6674
6971
|
const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
|
|
6675
6972
|
setExistingSections(processedSections);
|
|
6676
|
-
const updatedRootTree =
|
|
6677
|
-
ancestryMode.tree,
|
|
6678
|
-
currentInputDesc,
|
|
6679
|
-
processedSections
|
|
6680
|
-
);
|
|
6973
|
+
const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
|
|
6681
6974
|
const extrasObj = {
|
|
6682
6975
|
...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
|
|
6683
6976
|
is_private: isPrivate
|
|
@@ -6719,6 +7012,7 @@ function CreateAncestryPanel({
|
|
|
6719
7012
|
isPrivate,
|
|
6720
7013
|
ancestryMode.abstraction_tree
|
|
6721
7014
|
));
|
|
7015
|
+
setHasUnsavedChanges(false);
|
|
6722
7016
|
if (onRenderFullAncestry) {
|
|
6723
7017
|
const rotation = branchStack.reduce((acc, step) => {
|
|
6724
7018
|
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
@@ -6770,6 +7064,7 @@ function CreateAncestryPanel({
|
|
|
6770
7064
|
updatedRootTree,
|
|
6771
7065
|
extrasObj
|
|
6772
7066
|
);
|
|
7067
|
+
setHasUnsavedChanges(false);
|
|
6773
7068
|
setLastSavedSnapshot(takeSnapshot(
|
|
6774
7069
|
updatedRootTree,
|
|
6775
7070
|
currentInputName,
|
|
@@ -6792,6 +7087,7 @@ function CreateAncestryPanel({
|
|
|
6792
7087
|
const newTreeString = JSON.stringify(newRootTree);
|
|
6793
7088
|
if (currentTreeString !== newTreeString) {
|
|
6794
7089
|
updateGlobalTree(newRootTree);
|
|
7090
|
+
setHasUnsavedChanges(true);
|
|
6795
7091
|
}
|
|
6796
7092
|
}, [description, existingSections]);
|
|
6797
7093
|
const handleTriggerFullRender = () => {
|
|
@@ -6814,6 +7110,7 @@ function CreateAncestryPanel({
|
|
|
6814
7110
|
};
|
|
6815
7111
|
const handleSaveDescriptionInline = (newDesc) => {
|
|
6816
7112
|
setDescription(newDesc);
|
|
7113
|
+
setHasUnsavedChanges(true);
|
|
6817
7114
|
handleLocalSave(true, { description: newDesc });
|
|
6818
7115
|
};
|
|
6819
7116
|
const swallow = (e) => e.stopPropagation();
|
|
@@ -6943,7 +7240,11 @@ function CreateAncestryPanel({
|
|
|
6943
7240
|
{
|
|
6944
7241
|
type: "text",
|
|
6945
7242
|
value: ancestryName,
|
|
6946
|
-
onChange: (e) =>
|
|
7243
|
+
onChange: (e) => {
|
|
7244
|
+
setAncestryName(e.target.value);
|
|
7245
|
+
setHasUnsavedChanges(true);
|
|
7246
|
+
},
|
|
7247
|
+
readOnly: isContextLinked,
|
|
6947
7248
|
placeholder: "Nome da Ancestralidade",
|
|
6948
7249
|
className: "text-xl sm:text-2xl font-semibold tracking-tight bg-transparent border-none p-0 focus:ring-2 focus:ring-indigo-500 rounded-md -ml-1.5 px-1.5 w-full outline-none transition-all focus:bg-slate-800/70"
|
|
6949
7250
|
}
|
|
@@ -7431,7 +7732,7 @@ function ColorPicker({ color, onChange, disabled }) {
|
|
|
7431
7732
|
style: { backgroundColor: preset },
|
|
7432
7733
|
title: preset
|
|
7433
7734
|
},
|
|
7434
|
-
color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ React12.createElement(FiCheck6, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
|
|
7735
|
+
(color || "").toLowerCase() === (preset || "").toLowerCase() && /* @__PURE__ */ React12.createElement(FiCheck6, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
|
|
7435
7736
|
)))), /* @__PURE__ */ React12.createElement("style", null, `
|
|
7436
7737
|
.custom-react-colorful .react-colorful {
|
|
7437
7738
|
width: 100%;
|
|
@@ -7523,13 +7824,23 @@ function InSceneCreationForm({
|
|
|
7523
7824
|
}, [hasImages, useImageAsTexture, onImageChange]);
|
|
7524
7825
|
useEffect13(() => {
|
|
7525
7826
|
let result = [];
|
|
7827
|
+
const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
|
|
7526
7828
|
if (typeInput.trim() === "") {
|
|
7527
|
-
result =
|
|
7829
|
+
result = validExistingTypes.filter((t) => !types.includes(t));
|
|
7528
7830
|
} else {
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7831
|
+
console.log("InSceneCreationForm: Filtrando tipos com input:", typeInput);
|
|
7832
|
+
try {
|
|
7833
|
+
const lowercasedInput = typeInput.toLowerCase();
|
|
7834
|
+
result = validExistingTypes.filter((t) => {
|
|
7835
|
+
if (!t) {
|
|
7836
|
+
console.warn("InSceneCreationForm: Tipo encontrado como undefined/null durante filtragem");
|
|
7837
|
+
return false;
|
|
7838
|
+
}
|
|
7839
|
+
return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
|
|
7840
|
+
});
|
|
7841
|
+
} catch (err) {
|
|
7842
|
+
console.error("InSceneCreationForm: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
|
|
7843
|
+
}
|
|
7533
7844
|
}
|
|
7534
7845
|
if (sourceTypes) {
|
|
7535
7846
|
const priorityTypes = Array.isArray(sourceTypes) ? sourceTypes : [sourceTypes];
|
|
@@ -7592,9 +7903,9 @@ function InSceneCreationForm({
|
|
|
7592
7903
|
};
|
|
7593
7904
|
const handleToggleImageMode = () => {
|
|
7594
7905
|
var _a2, _b;
|
|
7595
|
-
const
|
|
7596
|
-
setUseImageAsTexture(
|
|
7597
|
-
if (
|
|
7906
|
+
const newValue2 = !useImageAsTexture;
|
|
7907
|
+
setUseImageAsTexture(newValue2);
|
|
7908
|
+
if (newValue2) {
|
|
7598
7909
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7599
7910
|
if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
|
|
7600
7911
|
const url = firstImageProp.value[0].value;
|
|
@@ -7892,9 +8203,9 @@ function InSceneVersionForm({
|
|
|
7892
8203
|
};
|
|
7893
8204
|
const handleToggleImageMode = () => {
|
|
7894
8205
|
var _a, _b;
|
|
7895
|
-
const
|
|
7896
|
-
setUseImageAsTexture(
|
|
7897
|
-
if (
|
|
8206
|
+
const newValue2 = !useImageAsTexture;
|
|
8207
|
+
setUseImageAsTexture(newValue2);
|
|
8208
|
+
if (newValue2) {
|
|
7898
8209
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7899
8210
|
if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
|
|
7900
8211
|
const url = firstImageProp.value[0].value;
|
|
@@ -8048,7 +8359,7 @@ function InSceneVersionForm({
|
|
|
8048
8359
|
|
|
8049
8360
|
// src/components/InSceneQuestForm.jsx
|
|
8050
8361
|
import React15, { useState as useState16, useRef as useRef12 } from "react";
|
|
8051
|
-
import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5 } from "react-icons/fi";
|
|
8362
|
+
import { FiPlus as FiPlus5, FiCheck as FiCheck9, FiEdit2 as FiEdit26, FiTarget, FiX as FiX4, FiChevronDown as FiChevronDown5, FiUser, FiSearch as FiSearch4 } from "react-icons/fi";
|
|
8052
8363
|
var QUEST_STATUS_COLORS2 = {
|
|
8053
8364
|
"Backlog": "#64748b",
|
|
8054
8365
|
"In Progress": "#eab308",
|
|
@@ -8070,10 +8381,13 @@ function InSceneQuestForm({
|
|
|
8070
8381
|
onSizeChange,
|
|
8071
8382
|
viewName = "Projeto",
|
|
8072
8383
|
// NOVA PROP
|
|
8073
|
-
questCounter = 1
|
|
8384
|
+
questCounter = 1,
|
|
8074
8385
|
// NOVA PROP
|
|
8386
|
+
viewMembers = []
|
|
8075
8387
|
}) {
|
|
8388
|
+
var _a, _b;
|
|
8076
8389
|
const [name, setName] = useState16("");
|
|
8390
|
+
const [assigneeId, setAssigneeId] = useState16("");
|
|
8077
8391
|
const [types, setTypes] = useState16(["quest"]);
|
|
8078
8392
|
const [typeInput, setTypeInput] = useState16("");
|
|
8079
8393
|
const [status, setStatus] = useState16("Backlog");
|
|
@@ -8081,6 +8395,8 @@ function InSceneQuestForm({
|
|
|
8081
8395
|
const [intensity, setIntensity] = useState16(0);
|
|
8082
8396
|
const [description, setDescription] = useState16("");
|
|
8083
8397
|
const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState16(false);
|
|
8398
|
+
const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState16(false);
|
|
8399
|
+
const [assigneeSearchQuery, setAssigneeSearchQuery] = useState16("");
|
|
8084
8400
|
const [customProps, setCustomProps] = useState16([]);
|
|
8085
8401
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState16(false);
|
|
8086
8402
|
const propsEndRef = useRef12(null);
|
|
@@ -8089,8 +8405,8 @@ function InSceneQuestForm({
|
|
|
8089
8405
|
const newProp = createNewCustomProperty(customProps);
|
|
8090
8406
|
setCustomProps([...customProps, newProp]);
|
|
8091
8407
|
setTimeout(() => {
|
|
8092
|
-
var
|
|
8093
|
-
(
|
|
8408
|
+
var _a2;
|
|
8409
|
+
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
8094
8410
|
}, 100);
|
|
8095
8411
|
};
|
|
8096
8412
|
const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
|
|
@@ -8136,6 +8452,7 @@ function InSceneQuestForm({
|
|
|
8136
8452
|
type: types,
|
|
8137
8453
|
color: QUEST_STATUS_COLORS2[status],
|
|
8138
8454
|
status,
|
|
8455
|
+
assignee_id: assigneeId || null,
|
|
8139
8456
|
size,
|
|
8140
8457
|
intensity,
|
|
8141
8458
|
description: description.trim(),
|
|
@@ -8207,7 +8524,59 @@ function InSceneQuestForm({
|
|
|
8207
8524
|
},
|
|
8208
8525
|
/* @__PURE__ */ React15.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
|
|
8209
8526
|
s
|
|
8210
|
-
)))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "
|
|
8527
|
+
)))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ React15.createElement("div", { className: "relative" }, /* @__PURE__ */ React15.createElement(
|
|
8528
|
+
"button",
|
|
8529
|
+
{
|
|
8530
|
+
type: "button",
|
|
8531
|
+
onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
|
|
8532
|
+
className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
|
|
8533
|
+
},
|
|
8534
|
+
/* @__PURE__ */ React15.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React15.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _a.name) || ((_b = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _b.email) || "Nenhum")),
|
|
8535
|
+
/* @__PURE__ */ React15.createElement(FiChevronDown5, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
|
|
8536
|
+
), isAssigneeDropdownOpen && /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
|
|
8537
|
+
setIsAssigneeDropdownOpen(false);
|
|
8538
|
+
setAssigneeSearchQuery("");
|
|
8539
|
+
} }), /* @__PURE__ */ React15.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ React15.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ React15.createElement(FiSearch4, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ React15.createElement(
|
|
8540
|
+
"input",
|
|
8541
|
+
{
|
|
8542
|
+
type: "text",
|
|
8543
|
+
autoFocus: true,
|
|
8544
|
+
placeholder: "Buscar membro...",
|
|
8545
|
+
value: assigneeSearchQuery,
|
|
8546
|
+
onChange: (e) => setAssigneeSearchQuery(e.target.value),
|
|
8547
|
+
className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
|
|
8548
|
+
onClick: (e) => e.stopPropagation()
|
|
8549
|
+
}
|
|
8550
|
+
)), /* @__PURE__ */ React15.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React15.createElement(
|
|
8551
|
+
"li",
|
|
8552
|
+
{
|
|
8553
|
+
onClick: () => {
|
|
8554
|
+
setAssigneeId("");
|
|
8555
|
+
setIsAssigneeDropdownOpen(false);
|
|
8556
|
+
setAssigneeSearchQuery("");
|
|
8557
|
+
},
|
|
8558
|
+
className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!assigneeId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
|
|
8559
|
+
},
|
|
8560
|
+
"Nenhum"
|
|
8561
|
+
), viewMembers.filter((member) => {
|
|
8562
|
+
const search = assigneeSearchQuery.toLowerCase();
|
|
8563
|
+
return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
|
|
8564
|
+
}).map((member) => /* @__PURE__ */ React15.createElement(
|
|
8565
|
+
"li",
|
|
8566
|
+
{
|
|
8567
|
+
key: member.id,
|
|
8568
|
+
onClick: () => {
|
|
8569
|
+
setAssigneeId(member.id);
|
|
8570
|
+
setIsAssigneeDropdownOpen(false);
|
|
8571
|
+
setAssigneeSearchQuery("");
|
|
8572
|
+
},
|
|
8573
|
+
className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${assigneeId === member.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
|
|
8574
|
+
},
|
|
8575
|
+
member.name || member.email || member.id
|
|
8576
|
+
)), viewMembers.filter((member) => {
|
|
8577
|
+
const search = assigneeSearchQuery.toLowerCase();
|
|
8578
|
+
return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
|
|
8579
|
+
}).length === 0 && assigneeSearchQuery && /* @__PURE__ */ React15.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado")))))), /* @__PURE__ */ React15.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React15.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React15.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ React15.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ React15.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React15.createElement(FiX4, { size: 12 })))), /* @__PURE__ */ React15.createElement(
|
|
8211
8580
|
"input",
|
|
8212
8581
|
{
|
|
8213
8582
|
type: "text",
|
|
@@ -8314,6 +8683,7 @@ function NodeDetailsPanel({
|
|
|
8314
8683
|
return !!(node == null ? void 0 : node.useImageAsTexture);
|
|
8315
8684
|
});
|
|
8316
8685
|
const [selectedImageUrl, setSelectedImageUrl] = useState17((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8686
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState17(false);
|
|
8317
8687
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8318
8688
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8319
8689
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8351,6 +8721,7 @@ function NodeDetailsPanel({
|
|
|
8351
8721
|
else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
|
|
8352
8722
|
else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
|
|
8353
8723
|
setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8724
|
+
setHasUnsavedChanges(false);
|
|
8354
8725
|
}
|
|
8355
8726
|
}, [node]);
|
|
8356
8727
|
const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
|
|
@@ -8361,15 +8732,22 @@ function NodeDetailsPanel({
|
|
|
8361
8732
|
}
|
|
8362
8733
|
}, [hasImages, useImageAsTexture]);
|
|
8363
8734
|
useEffect15(() => {
|
|
8735
|
+
const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
|
|
8364
8736
|
if (typeInput.trim() === "") {
|
|
8365
|
-
setFilteredTypes(
|
|
8737
|
+
setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
|
|
8366
8738
|
} else {
|
|
8367
|
-
|
|
8368
|
-
|
|
8369
|
-
|
|
8370
|
-
|
|
8371
|
-
|
|
8372
|
-
|
|
8739
|
+
console.log("NodeDetailsPanel: Filtrando tipos com input:", typeInput);
|
|
8740
|
+
try {
|
|
8741
|
+
const lowercasedInput = typeInput.toLowerCase();
|
|
8742
|
+
setFilteredTypes(
|
|
8743
|
+
validExistingTypes.filter((t) => {
|
|
8744
|
+
if (!t) return false;
|
|
8745
|
+
return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
|
|
8746
|
+
})
|
|
8747
|
+
);
|
|
8748
|
+
} catch (err) {
|
|
8749
|
+
console.error("NodeDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
|
|
8750
|
+
}
|
|
8373
8751
|
}
|
|
8374
8752
|
}, [typeInput, existingTypes, types]);
|
|
8375
8753
|
const handleIntensityChangeLocal = (e) => {
|
|
@@ -8377,6 +8755,7 @@ function NodeDetailsPanel({
|
|
|
8377
8755
|
setIntensity(val);
|
|
8378
8756
|
onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
|
|
8379
8757
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
|
|
8758
|
+
setHasUnsavedChanges(true);
|
|
8380
8759
|
};
|
|
8381
8760
|
const handleCopyLink = () => {
|
|
8382
8761
|
if (!(node == null ? void 0 : node.id)) return;
|
|
@@ -8394,14 +8773,17 @@ function NodeDetailsPanel({
|
|
|
8394
8773
|
const v = e.target.value;
|
|
8395
8774
|
setName(v);
|
|
8396
8775
|
onNameChange == null ? void 0 : onNameChange(node.id, v);
|
|
8776
|
+
setHasUnsavedChanges(true);
|
|
8397
8777
|
};
|
|
8398
8778
|
const handleColorChange = (val) => {
|
|
8399
8779
|
setColor(val);
|
|
8400
8780
|
onColorChange == null ? void 0 : onColorChange(node.id, val);
|
|
8781
|
+
setHasUnsavedChanges(true);
|
|
8401
8782
|
};
|
|
8402
8783
|
const handleSizeChange = (newSize) => {
|
|
8403
8784
|
setSize(newSize);
|
|
8404
8785
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8786
|
+
setHasUnsavedChanges(true);
|
|
8405
8787
|
};
|
|
8406
8788
|
const handleAddType = (newType) => {
|
|
8407
8789
|
const trimmed = newType.trim();
|
|
@@ -8409,10 +8791,12 @@ function NodeDetailsPanel({
|
|
|
8409
8791
|
setTypes([...types, trimmed]);
|
|
8410
8792
|
setTypeInput("");
|
|
8411
8793
|
setShowTypeSuggestions(false);
|
|
8794
|
+
setHasUnsavedChanges(true);
|
|
8412
8795
|
}
|
|
8413
8796
|
};
|
|
8414
8797
|
const handleRemoveType = (indexToRemove) => {
|
|
8415
8798
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
8799
|
+
setHasUnsavedChanges(true);
|
|
8416
8800
|
};
|
|
8417
8801
|
const handleTypeInputKeyDown = (e) => {
|
|
8418
8802
|
if (e.key === "Enter") {
|
|
@@ -8425,6 +8809,7 @@ function NodeDetailsPanel({
|
|
|
8425
8809
|
const handleAddProp = () => {
|
|
8426
8810
|
const newProp = createNewCustomProperty(customProps);
|
|
8427
8811
|
setCustomProps((p) => [...p, newProp]);
|
|
8812
|
+
setHasUnsavedChanges(true);
|
|
8428
8813
|
setTimeout(() => {
|
|
8429
8814
|
var _a;
|
|
8430
8815
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8433,19 +8818,21 @@ function NodeDetailsPanel({
|
|
|
8433
8818
|
const handleRemoveProp = (i) => {
|
|
8434
8819
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8435
8820
|
setCustomProps(newProps);
|
|
8821
|
+
setHasUnsavedChanges(true);
|
|
8436
8822
|
triggerAutoSave({ customProps: newProps });
|
|
8437
8823
|
};
|
|
8438
8824
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8439
8825
|
const newProps = [...customProps];
|
|
8440
8826
|
newProps[index] = updatedProp;
|
|
8441
8827
|
setCustomProps(newProps);
|
|
8828
|
+
setHasUnsavedChanges(true);
|
|
8442
8829
|
if (!updatedProp.isEditing) {
|
|
8443
8830
|
triggerAutoSave({ customProps: newProps });
|
|
8444
8831
|
}
|
|
8445
8832
|
};
|
|
8446
8833
|
const handleToggleImageMode = () => {
|
|
8447
|
-
const newValue = !useImageAsTexture;
|
|
8448
8834
|
setUseImageAsTexture(newValue);
|
|
8835
|
+
setHasUnsavedChanges(true);
|
|
8449
8836
|
let activeUrl = null;
|
|
8450
8837
|
if (newValue) {
|
|
8451
8838
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
@@ -8467,6 +8854,7 @@ function NodeDetailsPanel({
|
|
|
8467
8854
|
};
|
|
8468
8855
|
const handleSelectTexture = (url) => {
|
|
8469
8856
|
setSelectedImageUrl(url);
|
|
8857
|
+
setHasUnsavedChanges(true);
|
|
8470
8858
|
onImageChange == null ? void 0 : onImageChange(true, url, color);
|
|
8471
8859
|
onDataUpdate == null ? void 0 : onDataUpdate({
|
|
8472
8860
|
...node,
|
|
@@ -8477,6 +8865,7 @@ function NodeDetailsPanel({
|
|
|
8477
8865
|
};
|
|
8478
8866
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8479
8867
|
setDescription(newDescription);
|
|
8868
|
+
setHasUnsavedChanges(true);
|
|
8480
8869
|
onDataUpdate({ ...node, description: newDescription });
|
|
8481
8870
|
triggerAutoSave({ description: newDescription });
|
|
8482
8871
|
};
|
|
@@ -8487,6 +8876,10 @@ function NodeDetailsPanel({
|
|
|
8487
8876
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8488
8877
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8489
8878
|
const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
|
|
8879
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
8880
|
+
onClose();
|
|
8881
|
+
return;
|
|
8882
|
+
}
|
|
8490
8883
|
if (!currentName.trim() || currentTypes.length === 0) {
|
|
8491
8884
|
alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8492
8885
|
return;
|
|
@@ -8511,6 +8904,7 @@ function NodeDetailsPanel({
|
|
|
8511
8904
|
};
|
|
8512
8905
|
await onSave(dataToSave, keepOpen);
|
|
8513
8906
|
onDataUpdate(dataToSave);
|
|
8907
|
+
setHasUnsavedChanges(false);
|
|
8514
8908
|
if (!keepOpen) {
|
|
8515
8909
|
onClose();
|
|
8516
8910
|
}
|
|
@@ -8577,7 +8971,7 @@ function NodeDetailsPanel({
|
|
|
8577
8971
|
onImageClick: handleImageClickFromText,
|
|
8578
8972
|
onSaveDescription: handleSaveDescriptionInline
|
|
8579
8973
|
}
|
|
8580
|
-
) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div",
|
|
8974
|
+
) : /* @__PURE__ */ React16.createElement(React16.Fragment, null, /* @__PURE__ */ React16.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React16.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React16.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React16.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React16.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ React16.createElement(
|
|
8581
8975
|
"button",
|
|
8582
8976
|
{
|
|
8583
8977
|
onClick: handleCopyLink,
|
|
@@ -8585,7 +8979,7 @@ function NodeDetailsPanel({
|
|
|
8585
8979
|
title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
|
|
8586
8980
|
},
|
|
8587
8981
|
isLinkCopied ? /* @__PURE__ */ React16.createElement(FiCheck10, { size: 12 }) : /* @__PURE__ */ React16.createElement(FiLink5, { size: 12 })
|
|
8588
|
-
)), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
|
|
8982
|
+
)), /* @__PURE__ */ React16.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ React16.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React16.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React16.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React16.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ React16.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React16.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ React16.createElement(
|
|
8589
8983
|
"button",
|
|
8590
8984
|
{
|
|
8591
8985
|
type: "button",
|
|
@@ -8747,6 +9141,7 @@ function NodeDetailsPanel({
|
|
|
8747
9141
|
initialValue: description,
|
|
8748
9142
|
onSave: (newDescription) => {
|
|
8749
9143
|
setDescription(newDescription);
|
|
9144
|
+
setHasUnsavedChanges(true);
|
|
8750
9145
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
8751
9146
|
triggerAutoSave({ description: newDescription });
|
|
8752
9147
|
},
|
|
@@ -8760,7 +9155,7 @@ function NodeDetailsPanel({
|
|
|
8760
9155
|
|
|
8761
9156
|
// src/components/QuestDetailsPanel.jsx
|
|
8762
9157
|
import React17, { useState as useState18, useEffect as useEffect16, useRef as useRef14 } from "react";
|
|
8763
|
-
import { FiPlus as FiPlus7, FiX as FiX6, FiCheck as FiCheck11, FiEdit2 as FiEdit28, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4, FiLink as FiLink6, FiTarget as FiTarget2, FiChevronDown as FiChevronDown6 } from "react-icons/fi";
|
|
9158
|
+
import { FiPlus as FiPlus7, FiX as FiX6, FiCheck as FiCheck11, FiEdit2 as FiEdit28, FiLoader as FiLoader3, FiBookOpen as FiBookOpen4, FiLink as FiLink6, FiTarget as FiTarget2, FiChevronDown as FiChevronDown6, FiUser as FiUser2, FiSearch as FiSearch5 } from "react-icons/fi";
|
|
8764
9159
|
var QUEST_STATUS_COLORS3 = {
|
|
8765
9160
|
"Backlog": "#64748b",
|
|
8766
9161
|
"In Progress": "#eab308",
|
|
@@ -8783,7 +9178,8 @@ function QuestDetailsPanel({
|
|
|
8783
9178
|
onMentionClick,
|
|
8784
9179
|
onUploadFile,
|
|
8785
9180
|
userRole,
|
|
8786
|
-
currentDatasetName
|
|
9181
|
+
currentDatasetName,
|
|
9182
|
+
viewMembers = []
|
|
8787
9183
|
}) {
|
|
8788
9184
|
var _a;
|
|
8789
9185
|
const initialRawTitle = (node == null ? void 0 : node.raw_title) || (((_a = node == null ? void 0 : node.name) == null ? void 0 : _a.includes(" - \xBB ")) ? node.name.split(" - \xBB ")[1] : node == null ? void 0 : node.name) || "";
|
|
@@ -8795,9 +9191,12 @@ function QuestDetailsPanel({
|
|
|
8795
9191
|
const [typeInput, setTypeInput] = useState18("");
|
|
8796
9192
|
const [status, setStatus] = useState18((node == null ? void 0 : node.status) ?? "Backlog");
|
|
8797
9193
|
const [size, setSize] = useState18((node == null ? void 0 : node.size) ?? "medium");
|
|
9194
|
+
const [assigneeId, setAssigneeId] = useState18((node == null ? void 0 : node.assignee_id) || "");
|
|
8798
9195
|
const [description, setDescription] = useState18((node == null ? void 0 : node.description) ?? "");
|
|
8799
9196
|
const [intensity, setIntensity] = useState18((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8800
9197
|
const [isStatusDropdownOpen, setIsStatusDropdownOpen] = useState18(false);
|
|
9198
|
+
const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = useState18(false);
|
|
9199
|
+
const [assigneeSearchQuery, setAssigneeSearchQuery] = useState18("");
|
|
8801
9200
|
const [customProps, setCustomProps] = useState18(() => extractCustomPropsFromNode(node || {}));
|
|
8802
9201
|
const [showTypeSuggestions, setShowTypeSuggestions] = useState18(false);
|
|
8803
9202
|
const [filteredTypes, setFilteredTypes] = useState18([]);
|
|
@@ -8806,6 +9205,7 @@ function QuestDetailsPanel({
|
|
|
8806
9205
|
const [existingSections, setExistingSections] = useState18((node == null ? void 0 : node.description_sections) || []);
|
|
8807
9206
|
const [isSaving, setIsSaving] = useState18(false);
|
|
8808
9207
|
const [isLinkCopied, setIsLinkCopied] = useState18(false);
|
|
9208
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState18(false);
|
|
8809
9209
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8810
9210
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8811
9211
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8833,22 +9233,34 @@ function QuestDetailsPanel({
|
|
|
8833
9233
|
setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
|
|
8834
9234
|
setStatus((node == null ? void 0 : node.status) ?? "Backlog");
|
|
8835
9235
|
setSize((node == null ? void 0 : node.size) ?? "medium");
|
|
9236
|
+
setAssigneeId((node == null ? void 0 : node.assignee_id) || "");
|
|
8836
9237
|
setDescription((node == null ? void 0 : node.description) ?? "");
|
|
8837
9238
|
setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8838
9239
|
setExistingSections((node == null ? void 0 : node.description_sections) || []);
|
|
8839
9240
|
setCustomProps(extractCustomPropsFromNode(node || {}));
|
|
9241
|
+
setHasUnsavedChanges(false);
|
|
8840
9242
|
}
|
|
8841
9243
|
}, [node]);
|
|
8842
9244
|
useEffect16(() => {
|
|
9245
|
+
const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
|
|
8843
9246
|
if (typeInput.trim() === "") {
|
|
8844
|
-
setFilteredTypes(
|
|
9247
|
+
setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
|
|
8845
9248
|
} else {
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
9249
|
+
console.log("QuestDetailsPanel: Filtrando tipos com input:", typeInput);
|
|
9250
|
+
try {
|
|
9251
|
+
const lowercasedInput = typeInput.toLowerCase();
|
|
9252
|
+
setFilteredTypes(
|
|
9253
|
+
validExistingTypes.filter((t) => {
|
|
9254
|
+
if (!t) {
|
|
9255
|
+
console.warn("QuestDetailsPanel: Tipo encontrado como undefined/null durante filtragem");
|
|
9256
|
+
return false;
|
|
9257
|
+
}
|
|
9258
|
+
return t.toLowerCase().includes(lowercasedInput) && !types.includes(t);
|
|
9259
|
+
})
|
|
9260
|
+
);
|
|
9261
|
+
} catch (err) {
|
|
9262
|
+
console.error("QuestDetailsPanel: Erro ao filtrar tipos:", err, "typeInput:", typeInput);
|
|
9263
|
+
}
|
|
8852
9264
|
}
|
|
8853
9265
|
}, [typeInput, existingTypes, types]);
|
|
8854
9266
|
const handleCopyLink = () => {
|
|
@@ -8868,16 +9280,19 @@ function QuestDetailsPanel({
|
|
|
8868
9280
|
setRawTitle(val);
|
|
8869
9281
|
const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
|
|
8870
9282
|
onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
|
|
9283
|
+
setHasUnsavedChanges(true);
|
|
8871
9284
|
};
|
|
8872
9285
|
const handleSizeChange = (newSize) => {
|
|
8873
9286
|
setSize(newSize);
|
|
8874
9287
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
9288
|
+
setHasUnsavedChanges(true);
|
|
8875
9289
|
};
|
|
8876
9290
|
const handleStatusChange = (newStatus) => {
|
|
8877
9291
|
setStatus(newStatus);
|
|
8878
9292
|
const newColor = QUEST_STATUS_COLORS3[newStatus];
|
|
8879
9293
|
onColorChange == null ? void 0 : onColorChange(node.id, newColor);
|
|
8880
9294
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
|
|
9295
|
+
setHasUnsavedChanges(true);
|
|
8881
9296
|
};
|
|
8882
9297
|
const handleAddType = (newType) => {
|
|
8883
9298
|
const trimmed = newType.trim();
|
|
@@ -8885,11 +9300,13 @@ function QuestDetailsPanel({
|
|
|
8885
9300
|
setTypes([...types, trimmed]);
|
|
8886
9301
|
setTypeInput("");
|
|
8887
9302
|
setShowTypeSuggestions(false);
|
|
9303
|
+
setHasUnsavedChanges(true);
|
|
8888
9304
|
}
|
|
8889
9305
|
};
|
|
8890
9306
|
const handleRemoveType = (indexToRemove) => {
|
|
8891
9307
|
if (types[indexToRemove] === "quest") return;
|
|
8892
9308
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
9309
|
+
setHasUnsavedChanges(true);
|
|
8893
9310
|
};
|
|
8894
9311
|
const handleTypeInputKeyDown = (e) => {
|
|
8895
9312
|
if (e.key === "Enter") {
|
|
@@ -8902,6 +9319,7 @@ function QuestDetailsPanel({
|
|
|
8902
9319
|
const handleAddProp = () => {
|
|
8903
9320
|
const newProp = createNewCustomProperty(customProps);
|
|
8904
9321
|
setCustomProps((p) => [...p, newProp]);
|
|
9322
|
+
setHasUnsavedChanges(true);
|
|
8905
9323
|
setTimeout(() => {
|
|
8906
9324
|
var _a2;
|
|
8907
9325
|
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8910,18 +9328,21 @@ function QuestDetailsPanel({
|
|
|
8910
9328
|
const handleRemoveProp = (i) => {
|
|
8911
9329
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8912
9330
|
setCustomProps(newProps);
|
|
9331
|
+
setHasUnsavedChanges(true);
|
|
8913
9332
|
triggerAutoSave({ customProps: newProps });
|
|
8914
9333
|
};
|
|
8915
9334
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8916
9335
|
const newProps = [...customProps];
|
|
8917
9336
|
newProps[index] = updatedProp;
|
|
8918
9337
|
setCustomProps(newProps);
|
|
9338
|
+
setHasUnsavedChanges(true);
|
|
8919
9339
|
if (!updatedProp.isEditing) {
|
|
8920
9340
|
triggerAutoSave({ customProps: newProps });
|
|
8921
9341
|
}
|
|
8922
9342
|
};
|
|
8923
9343
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8924
9344
|
setDescription(newDescription);
|
|
9345
|
+
setHasUnsavedChanges(true);
|
|
8925
9346
|
onDataUpdate({ ...node, description: newDescription });
|
|
8926
9347
|
triggerAutoSave({ description: newDescription });
|
|
8927
9348
|
};
|
|
@@ -8929,10 +9350,15 @@ function QuestDetailsPanel({
|
|
|
8929
9350
|
const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
|
|
8930
9351
|
const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
|
|
8931
9352
|
const currentTypes = overrides.types !== void 0 ? overrides.types : types;
|
|
9353
|
+
const currentAssigneeId = overrides.assigneeId !== void 0 ? overrides.assigneeId : assigneeId;
|
|
8932
9354
|
const currentDescription = overrides.description !== void 0 ? overrides.description : description;
|
|
8933
9355
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8934
9356
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8935
9357
|
const currentStatus = overrides.status !== void 0 ? overrides.status : status;
|
|
9358
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9359
|
+
onClose();
|
|
9360
|
+
return;
|
|
9361
|
+
}
|
|
8936
9362
|
if (!currentRawTitle.trim() || currentTypes.length === 0) {
|
|
8937
9363
|
alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8938
9364
|
return;
|
|
@@ -8950,6 +9376,7 @@ function QuestDetailsPanel({
|
|
|
8950
9376
|
type: currentTypes,
|
|
8951
9377
|
color: QUEST_STATUS_COLORS3[currentStatus],
|
|
8952
9378
|
status: currentStatus,
|
|
9379
|
+
assignee_id: currentAssigneeId || null,
|
|
8953
9380
|
size,
|
|
8954
9381
|
description: currentDescription,
|
|
8955
9382
|
description_sections: processedSections,
|
|
@@ -8962,6 +9389,7 @@ function QuestDetailsPanel({
|
|
|
8962
9389
|
};
|
|
8963
9390
|
await onSave(dataToSave, keepOpen);
|
|
8964
9391
|
onDataUpdate(dataToSave);
|
|
9392
|
+
setHasUnsavedChanges(false);
|
|
8965
9393
|
if (!keepOpen) {
|
|
8966
9394
|
onClose();
|
|
8967
9395
|
}
|
|
@@ -8981,6 +9409,8 @@ function QuestDetailsPanel({
|
|
|
8981
9409
|
onClose();
|
|
8982
9410
|
};
|
|
8983
9411
|
const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
|
|
9412
|
+
const assigneeMember = viewMembers.find((m) => m.id === assigneeId);
|
|
9413
|
+
const isAssigneeUndefined = assigneeId && !assigneeMember;
|
|
8984
9414
|
return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(
|
|
8985
9415
|
"div",
|
|
8986
9416
|
{
|
|
@@ -9019,7 +9449,7 @@ function QuestDetailsPanel({
|
|
|
9019
9449
|
onImageClick: handleImageClickFromText,
|
|
9020
9450
|
onSaveDescription: handleSaveDescriptionInline
|
|
9021
9451
|
}
|
|
9022
|
-
) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div",
|
|
9452
|
+
) : /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React17.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React17.createElement(FiTarget2, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ React17.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ React17.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ React17.createElement(FiCheck11, { size: 12 }) : /* @__PURE__ */ React17.createElement(FiLink6, { size: 12 }))), /* @__PURE__ */ React17.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ React17.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ React17.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ React17.createElement(
|
|
9023
9453
|
"input",
|
|
9024
9454
|
{
|
|
9025
9455
|
type: "text",
|
|
@@ -9050,7 +9480,71 @@ function QuestDetailsPanel({
|
|
|
9050
9480
|
},
|
|
9051
9481
|
/* @__PURE__ */ React17.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS3[s] } }),
|
|
9052
9482
|
s
|
|
9053
|
-
)))))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "
|
|
9483
|
+
)))))), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), canEdit ? /* @__PURE__ */ React17.createElement("div", { className: "relative" }, /* @__PURE__ */ React17.createElement(
|
|
9484
|
+
"button",
|
|
9485
|
+
{
|
|
9486
|
+
type: "button",
|
|
9487
|
+
onClick: () => setIsAssigneeDropdownOpen(!isAssigneeDropdownOpen),
|
|
9488
|
+
className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 hover:border-white/20 focus:outline-none focus:ring-2 focus:ring-indigo-400/60 transition-colors flex items-center justify-between"
|
|
9489
|
+
},
|
|
9490
|
+
/* @__PURE__ */ React17.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(FiUser2, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ React17.createElement("span", { className: "text-slate-200 font-medium" }, isAssigneeUndefined ? "Undefined" : (assigneeMember == null ? void 0 : assigneeMember.name) || (assigneeMember == null ? void 0 : assigneeMember.email) || "Nenhum")),
|
|
9491
|
+
/* @__PURE__ */ React17.createElement(FiChevronDown6, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
|
|
9492
|
+
), isAssigneeDropdownOpen && /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
|
|
9493
|
+
setIsAssigneeDropdownOpen(false);
|
|
9494
|
+
setAssigneeSearchQuery("");
|
|
9495
|
+
} }), /* @__PURE__ */ React17.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ React17.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(FiSearch5, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ React17.createElement(
|
|
9496
|
+
"input",
|
|
9497
|
+
{
|
|
9498
|
+
type: "text",
|
|
9499
|
+
autoFocus: true,
|
|
9500
|
+
placeholder: "Buscar membro...",
|
|
9501
|
+
value: assigneeSearchQuery,
|
|
9502
|
+
onChange: (e) => setAssigneeSearchQuery(e.target.value),
|
|
9503
|
+
className: "bg-transparent border-none outline-none text-xs text-white placeholder-slate-500 w-full",
|
|
9504
|
+
onClick: (e) => e.stopPropagation()
|
|
9505
|
+
}
|
|
9506
|
+
)), /* @__PURE__ */ React17.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ React17.createElement(
|
|
9507
|
+
"li",
|
|
9508
|
+
{
|
|
9509
|
+
onClick: () => {
|
|
9510
|
+
setAssigneeId("");
|
|
9511
|
+
setHasUnsavedChanges(true);
|
|
9512
|
+
setIsAssigneeDropdownOpen(false);
|
|
9513
|
+
setAssigneeSearchQuery("");
|
|
9514
|
+
},
|
|
9515
|
+
className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${!assigneeId ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
|
|
9516
|
+
},
|
|
9517
|
+
"Nenhum"
|
|
9518
|
+
), isAssigneeUndefined && /* @__PURE__ */ React17.createElement(
|
|
9519
|
+
"li",
|
|
9520
|
+
{
|
|
9521
|
+
onClick: () => {
|
|
9522
|
+
setIsAssigneeDropdownOpen(false);
|
|
9523
|
+
setAssigneeSearchQuery("");
|
|
9524
|
+
},
|
|
9525
|
+
className: "px-3 py-2.5 text-sm cursor-pointer bg-red-500/10 text-red-300 flex items-center gap-2"
|
|
9526
|
+
},
|
|
9527
|
+
"Undefined (Removido)"
|
|
9528
|
+
), viewMembers.filter((member) => {
|
|
9529
|
+
const search = assigneeSearchQuery.toLowerCase();
|
|
9530
|
+
return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
|
|
9531
|
+
}).map((member) => /* @__PURE__ */ React17.createElement(
|
|
9532
|
+
"li",
|
|
9533
|
+
{
|
|
9534
|
+
key: member.id,
|
|
9535
|
+
onClick: () => {
|
|
9536
|
+
setAssigneeId(member.id);
|
|
9537
|
+
setHasUnsavedChanges(true);
|
|
9538
|
+
setIsAssigneeDropdownOpen(false);
|
|
9539
|
+
setAssigneeSearchQuery("");
|
|
9540
|
+
},
|
|
9541
|
+
className: `px-3 py-2.5 text-sm cursor-pointer transition-colors flex items-center gap-2 ${assigneeId === member.id ? "bg-indigo-500/20 text-white" : "text-slate-300 hover:bg-white/5 hover:text-white"}`
|
|
9542
|
+
},
|
|
9543
|
+
member.name || member.email || member.id
|
|
9544
|
+
)), viewMembers.filter((member) => {
|
|
9545
|
+
const search = assigneeSearchQuery.toLowerCase();
|
|
9546
|
+
return (member.name || "").toLowerCase().includes(search) || (member.email || "").toLowerCase().includes(search);
|
|
9547
|
+
}).length === 0 && assigneeSearchQuery && /* @__PURE__ */ React17.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado"))))) : /* @__PURE__ */ React17.createElement("div", { className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 text-slate-400 flex items-center gap-2" }, /* @__PURE__ */ React17.createElement(FiUser2, { className: "opacity-50", size: 14 }), assigneeId ? assigneeMember ? assigneeMember.name || assigneeMember.email : "Undefined" : "Nenhum")), /* @__PURE__ */ React17.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React17.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ React17.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ React17.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, canEdit && t !== "quest" && /* @__PURE__ */ React17.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ React17.createElement(FiX6, { size: 12 })))), canEdit && /* @__PURE__ */ React17.createElement(
|
|
9054
9548
|
"input",
|
|
9055
9549
|
{
|
|
9056
9550
|
type: "text",
|
|
@@ -9193,9 +9687,12 @@ function RelationshipDetailsPanel({
|
|
|
9193
9687
|
const [description, setDescription] = useState20((link == null ? void 0 : link.description) ?? "");
|
|
9194
9688
|
const [customProps, setCustomProps] = useState20(() => extractCustomPropsFromNode(link || {}));
|
|
9195
9689
|
const [existingSections, setExistingSections] = useState20((link == null ? void 0 : link.description_sections) || []);
|
|
9690
|
+
const [sourceLabel, setSourceLabel] = useState20((link == null ? void 0 : link.source_label) ?? "");
|
|
9691
|
+
const [targetLabel, setTargetLabel] = useState20((link == null ? void 0 : link.target_label) ?? "");
|
|
9196
9692
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState20(false);
|
|
9197
9693
|
const [isSaving, setIsSaving] = useState20(false);
|
|
9198
9694
|
const [isReadMode, setIsReadMode] = useState20(false);
|
|
9695
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = useState20(false);
|
|
9199
9696
|
const propsEndRef = useRef16(null);
|
|
9200
9697
|
const canEdit = useMemo9(() => {
|
|
9201
9698
|
const ability = defineAbilityFor(userRole);
|
|
@@ -9206,12 +9703,16 @@ function RelationshipDetailsPanel({
|
|
|
9206
9703
|
setDescription((link == null ? void 0 : link.description) ?? "");
|
|
9207
9704
|
setExistingSections((link == null ? void 0 : link.description_sections) || []);
|
|
9208
9705
|
setCustomProps(extractCustomPropsFromNode(link || {}));
|
|
9706
|
+
setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
|
|
9707
|
+
setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
|
|
9708
|
+
setHasUnsavedChanges(false);
|
|
9209
9709
|
}, [link]);
|
|
9210
9710
|
const swallow = (e) => e.stopPropagation();
|
|
9211
9711
|
const handleAddProp = () => {
|
|
9212
9712
|
if (!canEdit) return;
|
|
9213
9713
|
const newProp = createNewCustomProperty(customProps);
|
|
9214
9714
|
setCustomProps((p) => [...p, newProp]);
|
|
9715
|
+
setHasUnsavedChanges(true);
|
|
9215
9716
|
setTimeout(() => {
|
|
9216
9717
|
var _a;
|
|
9217
9718
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -9223,6 +9724,12 @@ function RelationshipDetailsPanel({
|
|
|
9223
9724
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
9224
9725
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
9225
9726
|
const currentName = overrides.name !== void 0 ? overrides.name : name;
|
|
9727
|
+
const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
|
|
9728
|
+
const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
|
|
9729
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9730
|
+
onClose();
|
|
9731
|
+
return;
|
|
9732
|
+
}
|
|
9226
9733
|
setIsSaving(true);
|
|
9227
9734
|
try {
|
|
9228
9735
|
const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
|
|
@@ -9238,8 +9745,11 @@ function RelationshipDetailsPanel({
|
|
|
9238
9745
|
isCurved: link.isCurved,
|
|
9239
9746
|
curveOffset: link.curveOffset
|
|
9240
9747
|
};
|
|
9748
|
+
if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
|
|
9749
|
+
if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
|
|
9241
9750
|
await onSave(dataToSave, keepOpen);
|
|
9242
9751
|
onDataUpdate(dataToSave);
|
|
9752
|
+
setHasUnsavedChanges(false);
|
|
9243
9753
|
if (!keepOpen) {
|
|
9244
9754
|
onClose();
|
|
9245
9755
|
}
|
|
@@ -9253,18 +9763,21 @@ function RelationshipDetailsPanel({
|
|
|
9253
9763
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
9254
9764
|
if (!canEdit) return;
|
|
9255
9765
|
setDescription(newDescription);
|
|
9766
|
+
setHasUnsavedChanges(true);
|
|
9256
9767
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9257
9768
|
triggerAutoSave({ description: newDescription });
|
|
9258
9769
|
};
|
|
9259
9770
|
const handleRemoveProp = (i) => {
|
|
9260
9771
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
9261
9772
|
setCustomProps(newProps);
|
|
9773
|
+
setHasUnsavedChanges(true);
|
|
9262
9774
|
triggerAutoSave({ customProps: newProps });
|
|
9263
9775
|
};
|
|
9264
9776
|
const handleUpdateProp = (index, updatedProp) => {
|
|
9265
9777
|
const newProps = [...customProps];
|
|
9266
9778
|
newProps[index] = updatedProp;
|
|
9267
9779
|
setCustomProps(newProps);
|
|
9780
|
+
setHasUnsavedChanges(true);
|
|
9268
9781
|
if (!updatedProp.isEditing) {
|
|
9269
9782
|
triggerAutoSave({ customProps: newProps });
|
|
9270
9783
|
}
|
|
@@ -9312,19 +9825,52 @@ function RelationshipDetailsPanel({
|
|
|
9312
9825
|
onImageClick: handleImageClickFromText,
|
|
9313
9826
|
onSaveDescription: handleSaveDescriptionInline
|
|
9314
9827
|
}
|
|
9315
|
-
) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div",
|
|
9828
|
+
) : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React19.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React19.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ React19.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ React19.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React19.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ React19.createElement(
|
|
9316
9829
|
"input",
|
|
9317
9830
|
{
|
|
9318
9831
|
type: "text",
|
|
9319
9832
|
value: name,
|
|
9320
|
-
onChange: (e) =>
|
|
9833
|
+
onChange: (e) => {
|
|
9834
|
+
setName(e.target.value);
|
|
9835
|
+
setHasUnsavedChanges(true);
|
|
9836
|
+
},
|
|
9321
9837
|
placeholder: "Ex: Controla, Pertence a, Fornece...",
|
|
9322
9838
|
disabled: !canEdit,
|
|
9323
9839
|
className: `w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60
|
|
9324
9840
|
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9325
9841
|
`
|
|
9326
9842
|
}
|
|
9327
|
-
)), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.
|
|
9843
|
+
)), /* @__PURE__ */ React19.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React19.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React19.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React19.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ React19.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ React19.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ React19.createElement(
|
|
9844
|
+
"input",
|
|
9845
|
+
{
|
|
9846
|
+
type: "text",
|
|
9847
|
+
value: sourceLabel,
|
|
9848
|
+
onChange: (e) => {
|
|
9849
|
+
setSourceLabel(e.target.value);
|
|
9850
|
+
setHasUnsavedChanges(true);
|
|
9851
|
+
},
|
|
9852
|
+
placeholder: "Ex: Conceitos",
|
|
9853
|
+
disabled: !canEdit,
|
|
9854
|
+
className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
|
|
9855
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9856
|
+
`
|
|
9857
|
+
}
|
|
9858
|
+
)), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ React19.createElement(
|
|
9859
|
+
"input",
|
|
9860
|
+
{
|
|
9861
|
+
type: "text",
|
|
9862
|
+
value: targetLabel,
|
|
9863
|
+
onChange: (e) => {
|
|
9864
|
+
setTargetLabel(e.target.value);
|
|
9865
|
+
setHasUnsavedChanges(true);
|
|
9866
|
+
},
|
|
9867
|
+
placeholder: "Ex: Refer\xEAncias",
|
|
9868
|
+
disabled: !canEdit,
|
|
9869
|
+
className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
|
|
9870
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9871
|
+
`
|
|
9872
|
+
}
|
|
9873
|
+
)))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
|
|
9328
9874
|
DescriptionDisplay,
|
|
9329
9875
|
{
|
|
9330
9876
|
description,
|
|
@@ -9394,6 +9940,7 @@ function RelationshipDetailsPanel({
|
|
|
9394
9940
|
onSave: (newDescription) => {
|
|
9395
9941
|
if (!canEdit) return;
|
|
9396
9942
|
setDescription(newDescription);
|
|
9943
|
+
setHasUnsavedChanges(true);
|
|
9397
9944
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9398
9945
|
triggerAutoSave({ description: newDescription });
|
|
9399
9946
|
},
|
|
@@ -9841,7 +10388,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
|
|
|
9841
10388
|
onMentionClick,
|
|
9842
10389
|
onImageClick: handleImageClickFromText
|
|
9843
10390
|
}
|
|
9844
|
-
) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div",
|
|
10391
|
+
) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ React23.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ React23.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ React23.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ React23.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ React23.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ React23.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ React23.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ React23.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ React23.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ React23.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React23.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React23.createElement(
|
|
9845
10392
|
"button",
|
|
9846
10393
|
{
|
|
9847
10394
|
onClick: () => setIsReadMode(true),
|
|
@@ -9875,7 +10422,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
|
|
|
9875
10422
|
// src/components/AncestryBoard.jsx
|
|
9876
10423
|
import React24, { useState as useState24, useMemo as useMemo11, useEffect as useEffect21, useRef as useRef18 } from "react";
|
|
9877
10424
|
import {
|
|
9878
|
-
FiSearch as
|
|
10425
|
+
FiSearch as FiSearch6,
|
|
9879
10426
|
FiLayers as FiLayers6,
|
|
9880
10427
|
FiCornerUpRight as FiCornerUpRight4,
|
|
9881
10428
|
FiPlay,
|
|
@@ -10000,7 +10547,7 @@ var GroupItem = ({
|
|
|
10000
10547
|
`,
|
|
10001
10548
|
title: "Adicionar Ancestralidade a este grupo"
|
|
10002
10549
|
},
|
|
10003
|
-
isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(
|
|
10550
|
+
isPickingForThisGroup ? /* @__PURE__ */ React24.createElement(FiCheckCircle, { size: 12 }) : /* @__PURE__ */ React24.createElement(FiSearch6, { size: 12 }),
|
|
10004
10551
|
isPickingForThisGroup ? "Selecionando..." : "Adicionar"
|
|
10005
10552
|
), /* @__PURE__ */ React24.createElement(
|
|
10006
10553
|
"button",
|
|
@@ -10335,7 +10882,7 @@ function AncestryBoard({
|
|
|
10335
10882
|
`
|
|
10336
10883
|
},
|
|
10337
10884
|
/* @__PURE__ */ React24.createElement("div", { className: "p-3 border-b border-white/5 bg-slate-900/50" }, /* @__PURE__ */ React24.createElement("div", { className: "relative group" }, /* @__PURE__ */ React24.createElement(
|
|
10338
|
-
|
|
10885
|
+
FiSearch6,
|
|
10339
10886
|
{
|
|
10340
10887
|
className: `absolute left-3 top-1/2 -translate-y-1/2 transition-colors ${pickingGroupId ? "text-indigo-400" : "text-slate-500 group-focus-within:text-indigo-400"}`
|
|
10341
10888
|
}
|
|
@@ -10493,7 +11040,8 @@ function XViewScene({
|
|
|
10493
11040
|
save_ancestry_board_action,
|
|
10494
11041
|
upload_file_action,
|
|
10495
11042
|
delete_file_action,
|
|
10496
|
-
check_user_permission
|
|
11043
|
+
check_user_permission,
|
|
11044
|
+
get_view_members
|
|
10497
11045
|
}) {
|
|
10498
11046
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
10499
11047
|
const { data: session, status } = useSession();
|
|
@@ -10526,6 +11074,13 @@ function XViewScene({
|
|
|
10526
11074
|
} else {
|
|
10527
11075
|
setPermissionStatus("denied");
|
|
10528
11076
|
setIsLoading(false);
|
|
11077
|
+
return;
|
|
11078
|
+
}
|
|
11079
|
+
if (get_view_members) {
|
|
11080
|
+
const membersRes = await get_view_members(owner_id, type, id);
|
|
11081
|
+
if (membersRes.success && membersRes.members) {
|
|
11082
|
+
setViewMembers(membersRes.members);
|
|
11083
|
+
}
|
|
10529
11084
|
}
|
|
10530
11085
|
} catch (error) {
|
|
10531
11086
|
console.error("Erro ao verificar permiss\xE3o:", error);
|
|
@@ -10566,6 +11121,7 @@ function XViewScene({
|
|
|
10566
11121
|
const [isLoading, setIsLoading] = useState25(true);
|
|
10567
11122
|
const [permissionStatus, setPermissionStatus] = useState25("loading");
|
|
10568
11123
|
const [userPermissionRole, setUserPermissionRole] = useState25(null);
|
|
11124
|
+
const [viewMembers, setViewMembers] = useState25([]);
|
|
10569
11125
|
const [isInitialized, setIsInitialized] = useState25(false);
|
|
10570
11126
|
const [sceneVersion, setSceneVersion] = useState25(0);
|
|
10571
11127
|
const [contextMenu, setContextMenu] = useState25({
|
|
@@ -10634,6 +11190,7 @@ function XViewScene({
|
|
|
10634
11190
|
});
|
|
10635
11191
|
const [isImportModalOpen, setIsImportModalOpen] = useState25(false);
|
|
10636
11192
|
const [importSuccessMessage, setImportSuccessMessage] = useState25("");
|
|
11193
|
+
const [invalidTargetError, setInvalidTargetError] = useState25(null);
|
|
10637
11194
|
const [highlightedNodeId, setHighlightedNodeId] = useState25(null);
|
|
10638
11195
|
const [isAncestryBoardOpen, setIsAncestryBoardOpen] = useState25(false);
|
|
10639
11196
|
const [ancestryBoardData, setAncestryBoardData] = useState25([]);
|
|
@@ -12482,6 +13039,19 @@ function XViewScene({
|
|
|
12482
13039
|
const handleStartVersioning = (nodeData) => {
|
|
12483
13040
|
userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
|
|
12484
13041
|
};
|
|
13042
|
+
const handleStartQuestQuick = useCallback4((questNode) => {
|
|
13043
|
+
var _a2;
|
|
13044
|
+
if (!questNode || !actionHandlerContext) return;
|
|
13045
|
+
const updatedNode = {
|
|
13046
|
+
...questNode,
|
|
13047
|
+
status: "In Progress",
|
|
13048
|
+
color: "#eab308",
|
|
13049
|
+
assignee_id: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id
|
|
13050
|
+
};
|
|
13051
|
+
if (userActionHandlers.handleSaveNodeDetails) {
|
|
13052
|
+
userActionHandlers.handleSaveNodeDetails(actionHandlerContext, updatedNode);
|
|
13053
|
+
}
|
|
13054
|
+
}, [session, actionHandlerContext]);
|
|
12485
13055
|
const handleCancelQuest = useCallback4(() => {
|
|
12486
13056
|
const { graphGroup, ghostElements } = stateRef.current;
|
|
12487
13057
|
if (ghostElements.node && graphGroup) {
|
|
@@ -14166,7 +14736,13 @@ function XViewScene({
|
|
|
14166
14736
|
if (!parentDataRef.current) {
|
|
14167
14737
|
return [];
|
|
14168
14738
|
}
|
|
14169
|
-
return Object.
|
|
14739
|
+
return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
|
|
14740
|
+
const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
|
|
14741
|
+
return (fileData.nodes || []).map((node) => ({
|
|
14742
|
+
...node,
|
|
14743
|
+
dataset_name: datasetName
|
|
14744
|
+
}));
|
|
14745
|
+
}).filter((node) => {
|
|
14170
14746
|
var _a2;
|
|
14171
14747
|
return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
|
|
14172
14748
|
});
|
|
@@ -14310,6 +14886,9 @@ function XViewScene({
|
|
|
14310
14886
|
}, 300);
|
|
14311
14887
|
} else {
|
|
14312
14888
|
setHasFocusedInitial(true);
|
|
14889
|
+
setInvalidTargetError(
|
|
14890
|
+
"O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
|
|
14891
|
+
);
|
|
14313
14892
|
}
|
|
14314
14893
|
}
|
|
14315
14894
|
}, [
|
|
@@ -14332,6 +14911,9 @@ function XViewScene({
|
|
|
14332
14911
|
}, 300);
|
|
14333
14912
|
} else {
|
|
14334
14913
|
setHasOpenedInitialAncestry(true);
|
|
14914
|
+
setInvalidTargetError(
|
|
14915
|
+
"O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
|
|
14916
|
+
);
|
|
14335
14917
|
}
|
|
14336
14918
|
}
|
|
14337
14919
|
}, [
|
|
@@ -14625,7 +15207,8 @@ function XViewScene({
|
|
|
14625
15207
|
availableNodes: allAvailableNodes,
|
|
14626
15208
|
availableAncestries: allAvailableAncestries,
|
|
14627
15209
|
viewName: viewParams == null ? void 0 : viewParams.name,
|
|
14628
|
-
questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1
|
|
15210
|
+
questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
|
|
15211
|
+
viewMembers
|
|
14629
15212
|
}
|
|
14630
15213
|
),
|
|
14631
15214
|
readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ React25.createElement(
|
|
@@ -14755,7 +15338,8 @@ function XViewScene({
|
|
|
14755
15338
|
onMentionClick: handleAddExistingNode,
|
|
14756
15339
|
onUploadFile: upload_file_action,
|
|
14757
15340
|
userRole: userPermissionRole,
|
|
14758
|
-
currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
|
|
15341
|
+
currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
|
|
15342
|
+
viewMembers
|
|
14759
15343
|
}
|
|
14760
15344
|
),
|
|
14761
15345
|
detailsNode && !detailsNode.is_quest && /* @__PURE__ */ React25.createElement(
|
|
@@ -14868,7 +15452,10 @@ function XViewScene({
|
|
|
14868
15452
|
onRenderAncestry: handleStartReadingAncestry,
|
|
14869
15453
|
onEditAncestry: handleEditAncestry,
|
|
14870
15454
|
onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
|
|
14871
|
-
onFocusNode: handleFocusNode
|
|
15455
|
+
onFocusNode: handleFocusNode,
|
|
15456
|
+
viewMembers,
|
|
15457
|
+
currentUser: session == null ? void 0 : session.user,
|
|
15458
|
+
onStartQuest: handleStartQuestQuick
|
|
14872
15459
|
}
|
|
14873
15460
|
),
|
|
14874
15461
|
/* @__PURE__ */ React25.createElement(
|
|
@@ -14941,6 +15528,83 @@ function XViewScene({
|
|
|
14941
15528
|
currentViewName: viewParams == null ? void 0 : viewParams.name,
|
|
14942
15529
|
currentAncestries: ancestryDataRef.current || []
|
|
14943
15530
|
}
|
|
15531
|
+
),
|
|
15532
|
+
invalidTargetError && /* @__PURE__ */ React25.createElement(
|
|
15533
|
+
"div",
|
|
15534
|
+
{
|
|
15535
|
+
className: "ui-overlay",
|
|
15536
|
+
style: {
|
|
15537
|
+
position: "fixed",
|
|
15538
|
+
top: "24px",
|
|
15539
|
+
left: "50%",
|
|
15540
|
+
transform: "translateX(-50%)",
|
|
15541
|
+
zIndex: 1e4,
|
|
15542
|
+
padding: "16px 24px",
|
|
15543
|
+
background: "rgba(30, 20, 20, 0.85)",
|
|
15544
|
+
backdropFilter: "blur(12px)",
|
|
15545
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
15546
|
+
border: "1px solid rgba(255, 70, 70, 0.35)",
|
|
15547
|
+
borderRadius: "16px",
|
|
15548
|
+
boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
|
|
15549
|
+
color: "#ffa0a0",
|
|
15550
|
+
display: "flex",
|
|
15551
|
+
alignItems: "center",
|
|
15552
|
+
gap: "16px",
|
|
15553
|
+
fontFamily: "Inter, sans-serif",
|
|
15554
|
+
animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
|
|
15555
|
+
}
|
|
15556
|
+
},
|
|
15557
|
+
/* @__PURE__ */ React25.createElement("style", null, `
|
|
15558
|
+
@keyframes fadeInDown {
|
|
15559
|
+
from { opacity: 0; transform: translate(-50%, -20px); }
|
|
15560
|
+
to { opacity: 1; transform: translate(-50%, 0); }
|
|
15561
|
+
}
|
|
15562
|
+
`),
|
|
15563
|
+
/* @__PURE__ */ React25.createElement(
|
|
15564
|
+
"svg",
|
|
15565
|
+
{
|
|
15566
|
+
width: "20",
|
|
15567
|
+
height: "20",
|
|
15568
|
+
viewBox: "0 0 24 24",
|
|
15569
|
+
fill: "none",
|
|
15570
|
+
stroke: "currentColor",
|
|
15571
|
+
strokeWidth: "2",
|
|
15572
|
+
strokeLinecap: "round",
|
|
15573
|
+
strokeLinejoin: "round"
|
|
15574
|
+
},
|
|
15575
|
+
/* @__PURE__ */ React25.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
15576
|
+
/* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
15577
|
+
/* @__PURE__ */ React25.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
15578
|
+
),
|
|
15579
|
+
/* @__PURE__ */ React25.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
|
|
15580
|
+
/* @__PURE__ */ React25.createElement(
|
|
15581
|
+
"button",
|
|
15582
|
+
{
|
|
15583
|
+
onClick: () => setInvalidTargetError(null),
|
|
15584
|
+
style: {
|
|
15585
|
+
background: "rgba(255, 255, 255, 0.1)",
|
|
15586
|
+
border: "none",
|
|
15587
|
+
color: "white",
|
|
15588
|
+
padding: "4px 10px",
|
|
15589
|
+
borderRadius: "8px",
|
|
15590
|
+
cursor: "pointer",
|
|
15591
|
+
fontSize: "12px",
|
|
15592
|
+
fontWeight: 600,
|
|
15593
|
+
transition: "all 0.2s",
|
|
15594
|
+
marginLeft: "8px",
|
|
15595
|
+
border: "1px solid rgba(255,255,255,0.1)"
|
|
15596
|
+
},
|
|
15597
|
+
onMouseOver: (e) => {
|
|
15598
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
|
|
15599
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
15600
|
+
},
|
|
15601
|
+
onMouseOut: (e) => {
|
|
15602
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
|
|
15603
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
15604
|
+
}
|
|
15605
|
+
},
|
|
15606
|
+
"Fechar"
|
|
15607
|
+
)
|
|
14944
15608
|
)
|
|
14945
15609
|
);
|
|
14946
15610
|
}
|
|
@@ -15367,6 +16031,17 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
|
|
|
15367
16031
|
return { success: false, error: error.message };
|
|
15368
16032
|
}
|
|
15369
16033
|
}
|
|
16034
|
+
async function get_view_members_logic(db_services, ownerId, type, itemId) {
|
|
16035
|
+
try {
|
|
16036
|
+
if (!db_services.get_view_members) {
|
|
16037
|
+
return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
|
|
16038
|
+
}
|
|
16039
|
+
return await db_services.get_view_members(ownerId, type, itemId);
|
|
16040
|
+
} catch (error) {
|
|
16041
|
+
console.error("Erro em get_view_members_logic:", error);
|
|
16042
|
+
return { success: false, error: error.message };
|
|
16043
|
+
}
|
|
16044
|
+
}
|
|
15370
16045
|
export {
|
|
15371
16046
|
XViewScene,
|
|
15372
16047
|
delete_uploaded_file_logic,
|
|
@@ -15374,6 +16049,7 @@ export {
|
|
|
15374
16049
|
get_ancestry_file_logic,
|
|
15375
16050
|
get_scene_view_data_logic,
|
|
15376
16051
|
get_single_parent_file_logic,
|
|
16052
|
+
get_view_members_logic,
|
|
15377
16053
|
import_parent_file_modal_get_logic,
|
|
15378
16054
|
save_ancestry_board_logic,
|
|
15379
16055
|
save_view_data_logic,
|