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