@lv-x-software-house/x_view 1.2.5-dev.2 → 1.2.5-dev.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +832 -155
- package/dist/index.mjs +836 -160
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,6 +35,7 @@ __export(index_exports, {
|
|
|
35
35
|
get_ancestry_file_logic: () => get_ancestry_file_logic,
|
|
36
36
|
get_scene_view_data_logic: () => get_scene_view_data_logic,
|
|
37
37
|
get_single_parent_file_logic: () => get_single_parent_file_logic,
|
|
38
|
+
get_view_members_logic: () => get_view_members_logic,
|
|
38
39
|
import_parent_file_modal_get_logic: () => import_parent_file_modal_get_logic,
|
|
39
40
|
save_ancestry_board_logic: () => save_ancestry_board_logic,
|
|
40
41
|
save_view_data_logic: () => save_view_data_logic,
|
|
@@ -141,7 +142,10 @@ function ContextMenu({
|
|
|
141
142
|
onEditAncestry,
|
|
142
143
|
onDeleteAncestry,
|
|
143
144
|
onFocusNode,
|
|
144
|
-
onClose
|
|
145
|
+
onClose,
|
|
146
|
+
viewMembers,
|
|
147
|
+
currentUser,
|
|
148
|
+
onStartQuest
|
|
145
149
|
}) {
|
|
146
150
|
var _a, _b, _c;
|
|
147
151
|
const menuRef = (0, import_react.useRef)(null);
|
|
@@ -149,6 +153,7 @@ function ContextMenu({
|
|
|
149
153
|
const [menuView, setMenuView] = (0, import_react.useState)("main");
|
|
150
154
|
const [selectedAncestry, setSelectedAncestry] = (0, import_react.useState)(null);
|
|
151
155
|
const [versionSubMenu, setVersionSubMenu] = (0, import_react.useState)(null);
|
|
156
|
+
const [labelSubMenu, setLabelSubMenu] = (0, import_react.useState)(null);
|
|
152
157
|
const [isLinkCopied, setIsLinkCopied] = (0, import_react.useState)(false);
|
|
153
158
|
const [selectedQuestStatus, setSelectedQuestStatus] = (0, import_react.useState)(null);
|
|
154
159
|
const ability = (0, import_react.useMemo)(() => defineAbilityFor(userRole), [userRole]);
|
|
@@ -157,6 +162,7 @@ function ContextMenu({
|
|
|
157
162
|
setMenuView("main");
|
|
158
163
|
setSelectedAncestry(null);
|
|
159
164
|
setVersionSubMenu(null);
|
|
165
|
+
setLabelSubMenu(null);
|
|
160
166
|
setSelectedQuestStatus(null);
|
|
161
167
|
}
|
|
162
168
|
}, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
|
|
@@ -172,7 +178,7 @@ function ContextMenu({
|
|
|
172
178
|
if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
|
|
173
179
|
if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
|
|
174
180
|
setMenuPos({ left, top });
|
|
175
|
-
}, [data, menuView, versionSubMenu, selectedQuestStatus]);
|
|
181
|
+
}, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
|
|
176
182
|
(0, import_react.useEffect)(() => {
|
|
177
183
|
if (!data.visible) return;
|
|
178
184
|
const handleClickOutside = (e) => {
|
|
@@ -224,7 +230,21 @@ function ContextMenu({
|
|
|
224
230
|
var _a2;
|
|
225
231
|
return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
|
|
226
232
|
});
|
|
227
|
-
const
|
|
233
|
+
const getLabelForConnection = (conn) => {
|
|
234
|
+
if (conn.direction === "outgoing") return conn.link.source_label || null;
|
|
235
|
+
if (conn.direction === "incoming") return conn.link.target_label || null;
|
|
236
|
+
return null;
|
|
237
|
+
};
|
|
238
|
+
const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
|
|
239
|
+
const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
|
|
240
|
+
const labelGroups = commonWithLabel.reduce((acc, conn) => {
|
|
241
|
+
const label = getLabelForConnection(conn);
|
|
242
|
+
if (!acc[label]) acc[label] = [];
|
|
243
|
+
acc[label].push(conn);
|
|
244
|
+
return acc;
|
|
245
|
+
}, {});
|
|
246
|
+
const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
|
|
247
|
+
const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
|
|
228
248
|
var _a2;
|
|
229
249
|
const { targetNode } = conn;
|
|
230
250
|
const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
|
|
@@ -326,12 +346,25 @@ function ContextMenu({
|
|
|
326
346
|
});
|
|
327
347
|
};
|
|
328
348
|
const renderMainView = () => {
|
|
329
|
-
var _a2;
|
|
349
|
+
var _a2, _b2, _c2;
|
|
330
350
|
const hasVersions = computedVersions.length > 0;
|
|
331
351
|
const canCreateVersion = ability.can("create", "Versioning");
|
|
332
352
|
const canReadVersion = ability.can("read", "Versioning");
|
|
333
353
|
const shouldShowVersioningBtn = canCreateVersion || canReadVersion && hasVersions;
|
|
334
|
-
|
|
354
|
+
const canStartQuest = isCurrentNodeQuest && (((_a2 = data.nodeData) == null ? void 0 : _a2.status) !== "In Progress" || ((_b2 = data.nodeData) == null ? void 0 : _b2.assignee_id) !== (currentUser == null ? void 0 : currentUser.id));
|
|
355
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_12px_1px_rgba(99,102,241,0.5)]" }), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "A\xE7\xF5es R\xE1pidas")), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, canStartQuest && /* @__PURE__ */ import_react.default.createElement(
|
|
356
|
+
"button",
|
|
357
|
+
{
|
|
358
|
+
onClick: () => {
|
|
359
|
+
onStartQuest == null ? void 0 : onStartQuest(data.nodeData);
|
|
360
|
+
onClose();
|
|
361
|
+
},
|
|
362
|
+
className: `w-full flex items-center gap-2.5 px-2 py-1.5 text-left text-sm rounded-md bg-yellow-500/10 text-yellow-400 hover:bg-yellow-500/20 hover:text-yellow-300 transition-colors duration-150 truncate font-semibold shadow-inner`,
|
|
363
|
+
title: "Atribuir a mim e iniciar"
|
|
364
|
+
},
|
|
365
|
+
/* @__PURE__ */ import_react.default.createElement("span", null, "\u{1F680}"),
|
|
366
|
+
/* @__PURE__ */ import_react.default.createElement("span", null, "Iniciar Quest")
|
|
367
|
+
), ability.can("create", "Connection") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartConnection == null ? void 0 : onStartConnection(data.nodeData), className: baseButtonClass, title: "Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conectar")), ability.can("create", "Node") && !((_c2 = data.nodeData) == null ? void 0 : _c2.is_quest) && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartCreation == null ? void 0 : onStartCreation(data.nodeData), className: baseButtonClass, title: "Criar e Conectar" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "16" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "8", y1: "12", x2: "16", y2: "12" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar e Conectar")), ability.can("create", "Ancestry") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onStartAncestryCreation == null ? void 0 : onStartAncestryCreation(data.nodeData), className: baseButtonClass, title: "Criar Ancestralidade" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 20.5c.5-.5.8-1.2.8-2s-.3-1.5-.8-2c-.5-.5-1.2-.8-2-.8s-1.5.3-2 .8c-.5.5-.8 1.2-.8 2s.3 1.5.8 2c.5.5 1.2-.8 2 .8s1.5-.3 2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 16v-3a2 2 0 0 1 2-2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 3.5c.5.5.8 1.2.8 2s-.3 1.5-.8 2c-.5-.5-1.2-.8-2 .8s1.5.3-2-.8c-.5-.5-.8-1.2-.8-2s.3-1.5.8-2c.5.5 1.2-.8 2 .8s1.5.3 2 .8Z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 8v3a2 2 0 0 0 2 2h4" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Criar Ancestralidade")), shouldShowVersioningBtn && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("versioning"), className: baseButtonClass, title: hasVersions ? "Versionamento" : "Criar Versionamento" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "6", y1: "3", x2: "6", y2: "15" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "6", r: "3" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "18", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 9a9 9 0 0 1-9 9" })), /* @__PURE__ */ import_react.default.createElement("span", null, hasVersions || !canCreateVersion ? "Versionamento" : "Criar Versionamento")), (connections.length > 0 || availableAncestries.length > 0) && ability.can("read", "Connection") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("connections"), className: baseButtonClass, title: "Conex\xF5es" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2z" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 12h8" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 8v8" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Conex\xF5es (", totalConnectionsCount, ")"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
335
368
|
onFocusNode == null ? void 0 : onFocusNode(data.nodeData);
|
|
336
369
|
onClose();
|
|
337
370
|
}, className: baseButtonClass, title: "Focar na c\xE2mera" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "10" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Focar neste Node")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: (e) => handleCopyLink(e, data.nodeData), className: baseButtonClass, title: "Copiar Link para Compartilhar" }, isLinkCopied ? /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "#4ade80", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "20 6 9 17 4 12" })) : /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.72" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.72-1.72" })), /* @__PURE__ */ import_react.default.createElement("span", { className: isLinkCopied ? "text-green-400" : "" }, isLinkCopied ? "Copiado!" : "Copiar Link")), ability.can("dismiss", "Node") && /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissNode == null ? void 0 : onDismissNode(data.nodeData), className: baseButtonClass, title: "Remover da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M9.88 9.88a3 3 0 1 0 4.24 4.24" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M10.73 5.08A10.43 10.43 0 0 1 12 5c7 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M6.61 6.61A13.526 13.526 0 0 0 2 12s3 7 10 7a9.74 9.74 0 0 0 5.39-1.61" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "2", y1: "2", x2: "22", y2: "22" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss")), /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => onDismissOtherNodes == null ? void 0 : onDismissOtherNodes(data.nodeData), className: baseButtonClass, title: "Remover outros da visualiza\xE7\xE3o" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "3" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M3 7V5a2 2 0 0 1 2-2h2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M17 3h2a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M21 17v2a2 2 0 0 1-2 2h-2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M7 21H5a2 2 0 0 1-2-2v-2" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Dismiss other nodes"))), ability.can("delete", "Node") && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("deleteConfirmation"), className: deleteButtonClass, title: "Excluir Node" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "3 6 5 6 21 6" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "10", y1: "11", x2: "10", y2: "17" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "14", y1: "11", x2: "14", y2: "17" })), /* @__PURE__ */ import_react.default.createElement("span", null, "Excluir Node"))));
|
|
@@ -352,11 +385,32 @@ function ContextMenu({
|
|
|
352
385
|
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
353
386
|
))));
|
|
354
387
|
};
|
|
388
|
+
const renderLabelSubMenuView = () => {
|
|
389
|
+
const group = labelSubMenu;
|
|
390
|
+
const isScrollable = group.connections.length > 10;
|
|
391
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => {
|
|
392
|
+
setLabelSubMenu(null);
|
|
393
|
+
setMenuView("connections");
|
|
394
|
+
}, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ import_react.default.createElement(
|
|
395
|
+
"button",
|
|
396
|
+
{
|
|
397
|
+
key: conn.targetNode.id,
|
|
398
|
+
onClick: () => handleExpandAndClose([conn.link]),
|
|
399
|
+
className: baseButtonClass,
|
|
400
|
+
title: `Expandir conex\xE3o com ${conn.targetNode.name}`
|
|
401
|
+
},
|
|
402
|
+
/* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ import_react.default.createElement("polyline", { points: "12 5 19 12 12 19" })),
|
|
403
|
+
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
404
|
+
))));
|
|
405
|
+
};
|
|
355
406
|
const renderConnectionsView = () => {
|
|
356
407
|
if (versionSubMenu) {
|
|
357
408
|
return renderVersionSubMenuView();
|
|
358
409
|
}
|
|
359
|
-
|
|
410
|
+
if (labelSubMenu) {
|
|
411
|
+
return renderLabelSubMenuView();
|
|
412
|
+
}
|
|
413
|
+
const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
|
|
360
414
|
const isScrollable = totalItems > 10;
|
|
361
415
|
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ import_react.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ import_react.default.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ import_react.default.createElement(
|
|
362
416
|
"button",
|
|
@@ -368,7 +422,20 @@ function ContextMenu({
|
|
|
368
422
|
},
|
|
369
423
|
/* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ import_react.default.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "21", r: "2" })),
|
|
370
424
|
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
|
|
371
|
-
))),
|
|
425
|
+
))), labelGroupEntries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ import_react.default.createElement(
|
|
426
|
+
"button",
|
|
427
|
+
{
|
|
428
|
+
key: label,
|
|
429
|
+
onClick: () => {
|
|
430
|
+
setLabelSubMenu({ label, connections: conns });
|
|
431
|
+
},
|
|
432
|
+
className: baseButtonClass,
|
|
433
|
+
title: `Ver grupo: ${label}`
|
|
434
|
+
},
|
|
435
|
+
/* @__PURE__ */ import_react.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400" }, /* @__PURE__ */ import_react.default.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ import_react.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
|
|
436
|
+
/* @__PURE__ */ import_react.default.createElement("span", { className: "flex-1 truncate" }, label),
|
|
437
|
+
/* @__PURE__ */ import_react.default.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
|
|
438
|
+
))), finalRenderableConnections.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ import_react.default.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
|
|
372
439
|
if (group.isVersionGroup) {
|
|
373
440
|
return /* @__PURE__ */ import_react.default.createElement(
|
|
374
441
|
"button",
|
|
@@ -552,15 +619,18 @@ function XViewSidebar({
|
|
|
552
619
|
const [isFilterMenuOpen, setIsFilterMenuOpen] = (0, import_react2.useState)(false);
|
|
553
620
|
const [filterCategory, setFilterCategory] = (0, import_react2.useState)("Type");
|
|
554
621
|
const [filterValue, setFilterValue] = (0, import_react2.useState)("");
|
|
622
|
+
const [isTypeDropdownOpen, setIsTypeDropdownOpen] = (0, import_react2.useState)(false);
|
|
623
|
+
const [highlightedTypeIndex, setHighlightedTypeIndex] = (0, import_react2.useState)(-1);
|
|
555
624
|
const containerRef = (0, import_react2.useRef)(null);
|
|
556
625
|
const inputRef = (0, import_react2.useRef)(null);
|
|
557
626
|
const filterMenuRef = (0, import_react2.useRef)(null);
|
|
627
|
+
const typeDropdownRef = (0, import_react2.useRef)(null);
|
|
558
628
|
const ability = (0, import_react2.useMemo)(() => {
|
|
559
629
|
return defineAbilityFor(userRole);
|
|
560
630
|
}, [userRole]);
|
|
561
631
|
const contextLabel = (0, import_react2.useMemo)(() => {
|
|
562
632
|
if (!viewType || !viewName) return null;
|
|
563
|
-
const typeLower = viewType.toLowerCase();
|
|
633
|
+
const typeLower = String(viewType).toLowerCase();
|
|
564
634
|
if (typeLower === "database") {
|
|
565
635
|
return `Dataset: ${viewName}`;
|
|
566
636
|
} else if (typeLower === "view") {
|
|
@@ -570,7 +640,12 @@ function XViewSidebar({
|
|
|
570
640
|
}
|
|
571
641
|
return `${viewType}: ${viewName}`;
|
|
572
642
|
}, [viewType, viewName]);
|
|
573
|
-
const normalize = (str = "") =>
|
|
643
|
+
const normalize = (str = "") => {
|
|
644
|
+
if (str === void 0 || str === null) {
|
|
645
|
+
console.warn("XViewSidebar: normalize recebeu valor nulo/indefinido:", str);
|
|
646
|
+
}
|
|
647
|
+
return String(str).toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[._\-–—:,;!?'"()\[\]{}/\\]/g, " ").replace(/\s+/g, " ").trim();
|
|
648
|
+
};
|
|
574
649
|
const collator = (0, import_react2.useMemo)(
|
|
575
650
|
() => new Intl.Collator("pt-BR", { sensitivity: "base", numeric: true }),
|
|
576
651
|
[]
|
|
@@ -579,13 +654,30 @@ function XViewSidebar({
|
|
|
579
654
|
const typesSet = /* @__PURE__ */ new Set();
|
|
580
655
|
(dbNodes || []).forEach((node) => {
|
|
581
656
|
if (Array.isArray(node.type)) {
|
|
582
|
-
node.type.forEach((t) =>
|
|
583
|
-
|
|
657
|
+
node.type.forEach((t) => {
|
|
658
|
+
if (t && typeof t === "string" && t.trim() !== "") typesSet.add(t);
|
|
659
|
+
});
|
|
660
|
+
} else if (node.type && typeof node.type === "string" && node.type.trim() !== "") {
|
|
584
661
|
typesSet.add(node.type);
|
|
585
662
|
}
|
|
586
663
|
});
|
|
587
664
|
return Array.from(typesSet).sort();
|
|
588
665
|
}, [dbNodes]);
|
|
666
|
+
const availableDatasets = (0, import_react2.useMemo)(() => {
|
|
667
|
+
const datasetsSet = /* @__PURE__ */ new Set();
|
|
668
|
+
(dbNodes || []).forEach((node) => {
|
|
669
|
+
if (node.dataset_name) {
|
|
670
|
+
datasetsSet.add(node.dataset_name);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
return Array.from(datasetsSet).sort();
|
|
674
|
+
}, [dbNodes]);
|
|
675
|
+
const filteredAutocompleteOptions = (0, import_react2.useMemo)(() => {
|
|
676
|
+
const source = filterCategory === "Dataset" ? availableDatasets : availableTypes;
|
|
677
|
+
if (!filterValue.trim()) return source;
|
|
678
|
+
const search = filterValue.toLowerCase().trim();
|
|
679
|
+
return source.filter((item) => item.toLowerCase().includes(search));
|
|
680
|
+
}, [availableTypes, availableDatasets, filterValue, filterCategory]);
|
|
589
681
|
const filteredAndSorted = (0, import_react2.useMemo)(() => {
|
|
590
682
|
const base = Array.isArray(dbNodes) ? dbNodes : [];
|
|
591
683
|
let pool = base;
|
|
@@ -605,6 +697,8 @@ function XViewSidebar({
|
|
|
605
697
|
return nodeTypes.some((t) => String(t).toLowerCase() === String(filter.value).toLowerCase());
|
|
606
698
|
} else if (filter.type === "Color") {
|
|
607
699
|
return String(node.color).toLowerCase() === String(filter.value).toLowerCase();
|
|
700
|
+
} else if (filter.type === "Dataset") {
|
|
701
|
+
return String(node.dataset_name || "").toLowerCase() === String(filter.value).toLowerCase();
|
|
608
702
|
}
|
|
609
703
|
return true;
|
|
610
704
|
});
|
|
@@ -620,18 +714,21 @@ function XViewSidebar({
|
|
|
620
714
|
inView.sort(byName);
|
|
621
715
|
const ordered = [...notInView, ...inView];
|
|
622
716
|
return ordered.slice(0, 200);
|
|
623
|
-
}, [dbNodes, query, isNodeInView, viewVersion, activeFilters]);
|
|
717
|
+
}, [dbNodes, query, isNodeInView, viewVersion, activeFilters, filterCategory]);
|
|
624
718
|
(0, import_react2.useEffect)(() => {
|
|
625
719
|
const handleClickOutside = (event) => {
|
|
626
720
|
if (filterMenuRef.current && !filterMenuRef.current.contains(event.target)) {
|
|
627
721
|
setIsFilterMenuOpen(false);
|
|
628
722
|
}
|
|
723
|
+
if (typeDropdownRef.current && !typeDropdownRef.current.contains(event.target)) {
|
|
724
|
+
setIsTypeDropdownOpen(false);
|
|
725
|
+
}
|
|
629
726
|
};
|
|
630
|
-
if (isFilterMenuOpen) {
|
|
727
|
+
if (isFilterMenuOpen || isTypeDropdownOpen) {
|
|
631
728
|
document.addEventListener("mousedown", handleClickOutside);
|
|
632
729
|
}
|
|
633
730
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
634
|
-
}, [isFilterMenuOpen]);
|
|
731
|
+
}, [isFilterMenuOpen, isTypeDropdownOpen]);
|
|
635
732
|
(0, import_react2.useEffect)(() => {
|
|
636
733
|
if (!query) setSelectedNodeId(null);
|
|
637
734
|
}, [query]);
|
|
@@ -685,6 +782,35 @@ function XViewSidebar({
|
|
|
685
782
|
const handleRemoveFilter = (index) => {
|
|
686
783
|
setActiveFilters((prev) => prev.filter((_, i) => i !== index));
|
|
687
784
|
};
|
|
785
|
+
const handleTypeKeyDown = (e) => {
|
|
786
|
+
if (!isTypeDropdownOpen) {
|
|
787
|
+
setIsTypeDropdownOpen(true);
|
|
788
|
+
}
|
|
789
|
+
if (e.key === "ArrowDown") {
|
|
790
|
+
e.preventDefault();
|
|
791
|
+
setHighlightedTypeIndex(
|
|
792
|
+
(prev) => prev < filteredAutocompleteOptions.length - 1 ? prev + 1 : prev
|
|
793
|
+
);
|
|
794
|
+
} else if (e.key === "ArrowUp") {
|
|
795
|
+
e.preventDefault();
|
|
796
|
+
setHighlightedTypeIndex((prev) => prev > 0 ? prev - 1 : 0);
|
|
797
|
+
} else if (e.key === "Enter") {
|
|
798
|
+
e.preventDefault();
|
|
799
|
+
if (highlightedTypeIndex >= 0 && filteredAutocompleteOptions[highlightedTypeIndex]) {
|
|
800
|
+
setFilterValue(filteredAutocompleteOptions[highlightedTypeIndex]);
|
|
801
|
+
setIsTypeDropdownOpen(false);
|
|
802
|
+
setHighlightedTypeIndex(-1);
|
|
803
|
+
} else {
|
|
804
|
+
handleAddFilter();
|
|
805
|
+
}
|
|
806
|
+
} else if (e.key === "Escape") {
|
|
807
|
+
e.preventDefault();
|
|
808
|
+
setIsTypeDropdownOpen(false);
|
|
809
|
+
setHighlightedTypeIndex(-1);
|
|
810
|
+
} else if (e.key === "Tab") {
|
|
811
|
+
setIsTypeDropdownOpen(false);
|
|
812
|
+
}
|
|
813
|
+
};
|
|
688
814
|
const ToggleButton = /* @__PURE__ */ import_react2.default.createElement(
|
|
689
815
|
"button",
|
|
690
816
|
{
|
|
@@ -704,7 +830,7 @@ function XViewSidebar({
|
|
|
704
830
|
"div",
|
|
705
831
|
{
|
|
706
832
|
ref: containerRef,
|
|
707
|
-
className: "ui-overlay fixed left-0 top-0 h-
|
|
833
|
+
className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
|
|
708
834
|
onPointerDown: swallow,
|
|
709
835
|
onPointerMove: swallow,
|
|
710
836
|
onPointerUp: swallow,
|
|
@@ -713,7 +839,7 @@ function XViewSidebar({
|
|
|
713
839
|
onContextMenu: swallow,
|
|
714
840
|
onDoubleClick: swallow
|
|
715
841
|
},
|
|
716
|
-
/* @__PURE__ */ import_react2.default.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
|
|
842
|
+
/* @__PURE__ */ import_react2.default.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ import_react2.default.createElement(
|
|
717
843
|
"button",
|
|
718
844
|
{
|
|
719
845
|
className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
@@ -770,7 +896,7 @@ function XViewSidebar({
|
|
|
770
896
|
autoComplete: "off"
|
|
771
897
|
}
|
|
772
898
|
)
|
|
773
|
-
), showList && /* @__PURE__ */ import_react2.default.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[
|
|
899
|
+
), showList && /* @__PURE__ */ import_react2.default.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
|
|
774
900
|
const inView = isNodeInView(n.id);
|
|
775
901
|
const active = selectedNodeId === n.id;
|
|
776
902
|
const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
|
|
@@ -804,7 +930,7 @@ function XViewSidebar({
|
|
|
804
930
|
},
|
|
805
931
|
/* @__PURE__ */ import_react2.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react2.default.createElement("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" })),
|
|
806
932
|
"Filters"
|
|
807
|
-
), isFilterMenuOpen && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color"].map((opt) => /* @__PURE__ */ import_react2.default.createElement(
|
|
933
|
+
), isFilterMenuOpen && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute right-0 top-full mt-2 w-56 p-3 rounded-lg bg-slate-900 border border-white/10 shadow-xl z-50 flex flex-col gap-3 animate-in fade-in zoom-in-95 duration-100" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Filtro"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex bg-slate-800/50 p-1 rounded-md" }, ["Type", "Color", "Dataset"].map((opt) => /* @__PURE__ */ import_react2.default.createElement(
|
|
808
934
|
"button",
|
|
809
935
|
{
|
|
810
936
|
key: opt,
|
|
@@ -815,18 +941,36 @@ function XViewSidebar({
|
|
|
815
941
|
className: `flex-1 text-xs py-1 rounded transition-colors ${filterCategory === opt ? "bg-indigo-600 text-white shadow-sm" : "text-slate-400 hover:text-slate-200"}`
|
|
816
942
|
},
|
|
817
943
|
opt
|
|
818
|
-
)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
944
|
+
)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-semibold tracking-wider" }, "Valor"), filterCategory === "Type" || filterCategory === "Dataset" ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative", ref: typeDropdownRef }, /* @__PURE__ */ import_react2.default.createElement(
|
|
819
945
|
"input",
|
|
820
946
|
{
|
|
821
|
-
list: "typeOptions",
|
|
822
947
|
type: "text",
|
|
823
948
|
value: filterValue,
|
|
824
|
-
onChange: (e) =>
|
|
825
|
-
|
|
949
|
+
onChange: (e) => {
|
|
950
|
+
setFilterValue(e.target.value);
|
|
951
|
+
if (!isTypeDropdownOpen) setIsTypeDropdownOpen(true);
|
|
952
|
+
setHighlightedTypeIndex(-1);
|
|
953
|
+
},
|
|
954
|
+
onFocus: () => setIsTypeDropdownOpen(true),
|
|
955
|
+
onKeyDown: handleTypeKeyDown,
|
|
956
|
+
placeholder: filterCategory === "Type" ? "Ex: Concept" : "Ex: Dataset 1",
|
|
826
957
|
className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white",
|
|
827
958
|
autoFocus: true
|
|
828
959
|
}
|
|
829
|
-
),
|
|
960
|
+
), isTypeDropdownOpen && filteredAutocompleteOptions.length > 0 && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute z-[60] left-0 right-0 mt-1 max-h-48 overflow-y-auto bg-slate-900/95 backdrop-blur-xl border border-white/10 rounded-md shadow-2xl custom-scrollbar" }, filteredAutocompleteOptions.map((t, idx) => /* @__PURE__ */ import_react2.default.createElement(
|
|
961
|
+
"div",
|
|
962
|
+
{
|
|
963
|
+
key: t,
|
|
964
|
+
className: `px-3 py-2 text-xs cursor-pointer transition-colors ${idx === highlightedTypeIndex ? "bg-indigo-600 text-white" : "text-slate-300 hover:bg-white/10"}`,
|
|
965
|
+
onMouseDown: (e) => {
|
|
966
|
+
e.preventDefault();
|
|
967
|
+
setFilterValue(t);
|
|
968
|
+
setIsTypeDropdownOpen(false);
|
|
969
|
+
setHighlightedTypeIndex(-1);
|
|
970
|
+
}
|
|
971
|
+
},
|
|
972
|
+
t
|
|
973
|
+
)))) : /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative flex items-center" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
830
974
|
"div",
|
|
831
975
|
{
|
|
832
976
|
className: "absolute left-2 w-3 h-3 rounded-full border border-white/20 shadow-sm",
|
|
@@ -2814,18 +2958,27 @@ function createNewCustomProperty(existingProps = []) {
|
|
|
2814
2958
|
};
|
|
2815
2959
|
}
|
|
2816
2960
|
var resolveDescriptionReference = (refString, availableNodes = [], availableAncestries = []) => {
|
|
2817
|
-
const match = refString.match(
|
|
2961
|
+
const match = refString.match(
|
|
2962
|
+
/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/
|
|
2963
|
+
);
|
|
2818
2964
|
if (!match) return null;
|
|
2819
2965
|
const [_, type, itemId, sectionId] = match;
|
|
2820
2966
|
let sourceItem = null;
|
|
2821
2967
|
if (type === "node") {
|
|
2822
2968
|
sourceItem = availableNodes.find((n) => String(n.id) === String(itemId));
|
|
2823
2969
|
} else {
|
|
2824
|
-
sourceItem = availableAncestries.find(
|
|
2970
|
+
sourceItem = availableAncestries.find(
|
|
2971
|
+
(a) => String(a.ancestry_id) === String(itemId)
|
|
2972
|
+
);
|
|
2825
2973
|
}
|
|
2826
2974
|
if (!sourceItem) return null;
|
|
2827
|
-
const sections = parseDescriptionSections(
|
|
2828
|
-
|
|
2975
|
+
const sections = parseDescriptionSections(
|
|
2976
|
+
sourceItem.description,
|
|
2977
|
+
sourceItem.description_sections
|
|
2978
|
+
);
|
|
2979
|
+
const targetSection = sections.find(
|
|
2980
|
+
(s) => String(s.id) === String(sectionId)
|
|
2981
|
+
);
|
|
2829
2982
|
if (!targetSection) return null;
|
|
2830
2983
|
return {
|
|
2831
2984
|
content: targetSection.content,
|
|
@@ -2839,21 +2992,34 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
|
|
|
2839
2992
|
let text = rawText;
|
|
2840
2993
|
const allNodes = parentData ? Object.values(parentData).flatMap((f) => f.nodes || []) : [];
|
|
2841
2994
|
const allAncestries = ancestryData || [];
|
|
2842
|
-
text = text.replace(
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2995
|
+
text = text.replace(
|
|
2996
|
+
/\[\[REF:(node|ancestry):([a-zA-Z0-9\-_]+):([a-zA-Z0-9\-_]+)\]\]/g,
|
|
2997
|
+
(match, type, itemId, sectionId) => {
|
|
2998
|
+
const resolved = resolveDescriptionReference(
|
|
2999
|
+
match,
|
|
3000
|
+
allNodes,
|
|
3001
|
+
allAncestries
|
|
3002
|
+
);
|
|
3003
|
+
if (resolved && !resolved.error) {
|
|
3004
|
+
return resolved.content;
|
|
3005
|
+
}
|
|
3006
|
+
return "[Refer\xEAncia indispon\xEDvel]";
|
|
2846
3007
|
}
|
|
2847
|
-
|
|
2848
|
-
});
|
|
3008
|
+
);
|
|
2849
3009
|
text = text.replace(/\*\/\s*\d+(?::[a-zA-Z0-9-]+)?\s*\//g, "");
|
|
2850
|
-
text = text.replace(
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
3010
|
+
text = text.replace(
|
|
3011
|
+
/\[\[MENTION:node:([a-zA-Z0-9\-_]+)\]\]/g,
|
|
3012
|
+
(match, nodeId) => {
|
|
3013
|
+
const node = allNodes.find((n) => String(n.id) === String(nodeId));
|
|
3014
|
+
return node ? `@${node.name}` : `@Men\xE7\xE3o`;
|
|
3015
|
+
}
|
|
3016
|
+
);
|
|
3017
|
+
text = text.replace(
|
|
3018
|
+
/\[\[MENTION:image:([^|\]]+)\|?([^\]]*)\]\]/g,
|
|
3019
|
+
(match, url, name) => {
|
|
3020
|
+
return name ? `[Imagem: ${name}]` : `[Imagem]`;
|
|
3021
|
+
}
|
|
3022
|
+
);
|
|
2857
3023
|
text = text.replace(/^#+\s*/gm, "");
|
|
2858
3024
|
text = text.replace(/```[\s\S]*?```/g, "[C\xF3digo]");
|
|
2859
3025
|
text = text.replace(/-\s\[[xX ]\]\s*/g, "\u2022 ");
|
|
@@ -2861,7 +3027,14 @@ function formatDescriptionForTooltip(rawText, parentData, ancestryData) {
|
|
|
2861
3027
|
return text.trim();
|
|
2862
3028
|
}
|
|
2863
3029
|
function generateTooltipHtml(data, parentData, ancestryData) {
|
|
2864
|
-
const ignoredKeys = [
|
|
3030
|
+
const ignoredKeys = [
|
|
3031
|
+
"id",
|
|
3032
|
+
"name",
|
|
3033
|
+
"type",
|
|
3034
|
+
"color",
|
|
3035
|
+
"_baseEmissiveIntensity",
|
|
3036
|
+
"description"
|
|
3037
|
+
];
|
|
2865
3038
|
const customKeys = Object.keys(data).filter((k) => !ignoredKeys.includes(k));
|
|
2866
3039
|
const extras = customKeys.length;
|
|
2867
3040
|
let typeDisplay = "Node";
|
|
@@ -2876,7 +3049,11 @@ function generateTooltipHtml(data, parentData, ancestryData) {
|
|
|
2876
3049
|
<div style="font-weight:600; font-size: 14px; color: #fff;">${typeDisplay}</div>
|
|
2877
3050
|
<div style="margin-bottom: 2px; color: #e2e8f0;">${data.name || ""}</div>`;
|
|
2878
3051
|
if (data.description) {
|
|
2879
|
-
const cleanDesc = formatDescriptionForTooltip(
|
|
3052
|
+
const cleanDesc = formatDescriptionForTooltip(
|
|
3053
|
+
data.description,
|
|
3054
|
+
parentData,
|
|
3055
|
+
ancestryData
|
|
3056
|
+
);
|
|
2880
3057
|
if (cleanDesc) {
|
|
2881
3058
|
html += `<div style="
|
|
2882
3059
|
margin-top: 6px;
|
|
@@ -2914,7 +3091,11 @@ function generateLinkTooltipHtml(data, parentData, ancestryData) {
|
|
|
2914
3091
|
html += `<div style="font-weight:600; font-size: 14px; color: #a5f3fc; margin-bottom: 4px;">${data.name}</div>`;
|
|
2915
3092
|
}
|
|
2916
3093
|
if (hasDescription) {
|
|
2917
|
-
const cleanDesc = formatDescriptionForTooltip(
|
|
3094
|
+
const cleanDesc = formatDescriptionForTooltip(
|
|
3095
|
+
data.description,
|
|
3096
|
+
parentData,
|
|
3097
|
+
ancestryData
|
|
3098
|
+
);
|
|
2918
3099
|
if (cleanDesc) {
|
|
2919
3100
|
html += `<div style="
|
|
2920
3101
|
display: -webkit-box;
|
|
@@ -2996,7 +3177,8 @@ var IGNORED_KEYS = [
|
|
|
2996
3177
|
"isAddingAbstractionNodes",
|
|
2997
3178
|
"status",
|
|
2998
3179
|
"is_quest",
|
|
2999
|
-
"raw_title"
|
|
3180
|
+
"raw_title",
|
|
3181
|
+
"assignee_id"
|
|
3000
3182
|
];
|
|
3001
3183
|
function extractCustomPropsFromNode(node) {
|
|
3002
3184
|
const customPropTypes = node._customPropTypes || {};
|
|
@@ -3006,16 +3188,40 @@ function extractCustomPropsFromNode(node) {
|
|
|
3006
3188
|
if (t === "date") {
|
|
3007
3189
|
if (val && typeof val === "object") {
|
|
3008
3190
|
if ("date_interval" in val) {
|
|
3009
|
-
return {
|
|
3191
|
+
return {
|
|
3192
|
+
id: v4_default(),
|
|
3193
|
+
key,
|
|
3194
|
+
type: "date",
|
|
3195
|
+
value: {
|
|
3196
|
+
type: "Date Interval",
|
|
3197
|
+
start: val.date_interval.start || "",
|
|
3198
|
+
end: val.date_interval.end || ""
|
|
3199
|
+
}
|
|
3200
|
+
};
|
|
3010
3201
|
}
|
|
3011
3202
|
if ("year" in val) {
|
|
3012
|
-
return {
|
|
3203
|
+
return {
|
|
3204
|
+
id: v4_default(),
|
|
3205
|
+
key,
|
|
3206
|
+
type: "date",
|
|
3207
|
+
value: { type: "Ano", value: val.year || "" }
|
|
3208
|
+
};
|
|
3013
3209
|
}
|
|
3014
3210
|
if ("date" in val) {
|
|
3015
|
-
return {
|
|
3211
|
+
return {
|
|
3212
|
+
id: v4_default(),
|
|
3213
|
+
key,
|
|
3214
|
+
type: "date",
|
|
3215
|
+
value: { type: "Data", value: val.date || "" }
|
|
3216
|
+
};
|
|
3016
3217
|
}
|
|
3017
3218
|
}
|
|
3018
|
-
return {
|
|
3219
|
+
return {
|
|
3220
|
+
id: v4_default(),
|
|
3221
|
+
key,
|
|
3222
|
+
type: "date",
|
|
3223
|
+
value: { type: "Data", value: "" }
|
|
3224
|
+
};
|
|
3019
3225
|
}
|
|
3020
3226
|
if (t === "list" || t === "links" || t === "images" || t === "documents") {
|
|
3021
3227
|
const safeList = (val || []).map((item) => ({
|
|
@@ -3025,7 +3231,12 @@ function extractCustomPropsFromNode(node) {
|
|
|
3025
3231
|
}));
|
|
3026
3232
|
return { id: v4_default(), key, type: t, value: safeList };
|
|
3027
3233
|
}
|
|
3028
|
-
return {
|
|
3234
|
+
return {
|
|
3235
|
+
id: v4_default(),
|
|
3236
|
+
key,
|
|
3237
|
+
type: t,
|
|
3238
|
+
value: t === "number" ? Number(val) || 0 : String(val ?? "")
|
|
3239
|
+
};
|
|
3029
3240
|
});
|
|
3030
3241
|
}
|
|
3031
3242
|
function toObjectFromCustomProps(customProps) {
|
|
@@ -3040,7 +3251,12 @@ function toObjectFromCustomProps(customProps) {
|
|
|
3040
3251
|
const { type: uiDateType, ...dateValues } = p.value || {};
|
|
3041
3252
|
if (uiDateType) {
|
|
3042
3253
|
if (uiDateType === "Date Interval") {
|
|
3043
|
-
out[key] = {
|
|
3254
|
+
out[key] = {
|
|
3255
|
+
date_interval: {
|
|
3256
|
+
start: dateValues.start || "",
|
|
3257
|
+
end: dateValues.end || ""
|
|
3258
|
+
}
|
|
3259
|
+
};
|
|
3044
3260
|
} else if (uiDateType === "Ano") {
|
|
3045
3261
|
out[key] = { year: dateValues.value || "" };
|
|
3046
3262
|
} else {
|
|
@@ -3076,10 +3292,20 @@ function createTextSprite(text, fontSize = 64) {
|
|
|
3076
3292
|
context.fillText(effectiveText, canvas.width / 2, canvas.height / 2);
|
|
3077
3293
|
const texture = new THREE2.CanvasTexture(canvas);
|
|
3078
3294
|
texture.colorSpace = THREE2.SRGBColorSpace;
|
|
3079
|
-
const spriteMaterial = new THREE2.SpriteMaterial({
|
|
3295
|
+
const spriteMaterial = new THREE2.SpriteMaterial({
|
|
3296
|
+
map: texture,
|
|
3297
|
+
sizeAttenuation: true,
|
|
3298
|
+
depthWrite: false,
|
|
3299
|
+
transparent: true,
|
|
3300
|
+
toneMapped: false
|
|
3301
|
+
});
|
|
3080
3302
|
const sprite = new THREE2.Sprite(spriteMaterial);
|
|
3081
3303
|
const scaleFactor = 0.05;
|
|
3082
|
-
sprite.scale.set(
|
|
3304
|
+
sprite.scale.set(
|
|
3305
|
+
canvas.width * scaleFactor,
|
|
3306
|
+
canvas.height * scaleFactor,
|
|
3307
|
+
1
|
|
3308
|
+
);
|
|
3083
3309
|
sprite.layers.set(DEFAULT_LAYER);
|
|
3084
3310
|
return sprite;
|
|
3085
3311
|
}
|
|
@@ -3095,7 +3321,14 @@ function makeGlowTexture() {
|
|
|
3095
3321
|
const canvas = document.createElement("canvas");
|
|
3096
3322
|
canvas.width = canvas.height = size;
|
|
3097
3323
|
const ctx = canvas.getContext("2d");
|
|
3098
|
-
const grd = ctx.createRadialGradient(
|
|
3324
|
+
const grd = ctx.createRadialGradient(
|
|
3325
|
+
size / 2,
|
|
3326
|
+
size / 2,
|
|
3327
|
+
0,
|
|
3328
|
+
size / 2,
|
|
3329
|
+
size / 2,
|
|
3330
|
+
size / 2
|
|
3331
|
+
);
|
|
3099
3332
|
grd.addColorStop(0, "rgba(255,255,255,1)");
|
|
3100
3333
|
grd.addColorStop(0.4, "rgba(255,255,255,0.45)");
|
|
3101
3334
|
grd.addColorStop(1, "rgba(255,255,255,0)");
|
|
@@ -3111,8 +3344,19 @@ function addGlowAura(mesh, hexColor, glowTexture, auraScale) {
|
|
|
3111
3344
|
let midBoost = 1 - Math.abs(L - 0.5) * 2;
|
|
3112
3345
|
midBoost = THREE2.MathUtils.clamp(midBoost, 0, 1);
|
|
3113
3346
|
const auraOpacity = THREE2.MathUtils.lerp(0.25, 0.8, midBoost);
|
|
3114
|
-
const auraColor = new THREE2.Color(hexColor).lerp(
|
|
3115
|
-
|
|
3347
|
+
const auraColor = new THREE2.Color(hexColor).lerp(
|
|
3348
|
+
new THREE2.Color("#ffffff"),
|
|
3349
|
+
0.25
|
|
3350
|
+
);
|
|
3351
|
+
const auraMat = new THREE2.SpriteMaterial({
|
|
3352
|
+
map: glowTexture,
|
|
3353
|
+
color: auraColor,
|
|
3354
|
+
opacity: auraOpacity,
|
|
3355
|
+
blending: THREE2.AdditiveBlending,
|
|
3356
|
+
transparent: true,
|
|
3357
|
+
depthWrite: false,
|
|
3358
|
+
toneMapped: false
|
|
3359
|
+
});
|
|
3116
3360
|
const aura = new THREE2.Sprite(auraMat);
|
|
3117
3361
|
aura.scale.setScalar(auraScale);
|
|
3118
3362
|
aura.name = "aura";
|
|
@@ -3145,7 +3389,17 @@ function calculateNodePositions(nodes) {
|
|
|
3145
3389
|
}
|
|
3146
3390
|
return positions;
|
|
3147
3391
|
}
|
|
3148
|
-
function updateTooltip({
|
|
3392
|
+
function updateTooltip({
|
|
3393
|
+
tooltipEl,
|
|
3394
|
+
hoveredNode,
|
|
3395
|
+
hoveredLink,
|
|
3396
|
+
camera,
|
|
3397
|
+
mountEl,
|
|
3398
|
+
isSceneBusy,
|
|
3399
|
+
parentData,
|
|
3400
|
+
ancestryData
|
|
3401
|
+
}) {
|
|
3402
|
+
var _a, _b;
|
|
3149
3403
|
if (!tooltipEl || !camera || !mountEl) return;
|
|
3150
3404
|
let content = "";
|
|
3151
3405
|
let positionTarget = null;
|
|
@@ -3155,20 +3409,35 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3155
3409
|
currentId = `node_${hoveredNode.userData.id}`;
|
|
3156
3410
|
positionTarget = hoveredNode;
|
|
3157
3411
|
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3158
|
-
content = generateTooltipHtml(
|
|
3412
|
+
content = generateTooltipHtml(
|
|
3413
|
+
hoveredNode.userData,
|
|
3414
|
+
parentData,
|
|
3415
|
+
ancestryData
|
|
3416
|
+
);
|
|
3159
3417
|
}
|
|
3160
3418
|
} else if (hoveredLink && !isSceneBusy) {
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3419
|
+
const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
|
|
3420
|
+
const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
|
|
3421
|
+
if (hasContent) {
|
|
3422
|
+
currentId = `link_${hoveredLink.userData.id}`;
|
|
3423
|
+
if (hoveredLink.userData.isCurved) {
|
|
3424
|
+
const positions = hoveredLink.geometry.attributes.position.array;
|
|
3425
|
+
const midIndex = Math.floor(positions.length / 2 / 3) * 3;
|
|
3426
|
+
positionTarget = new THREE2.Vector3(
|
|
3427
|
+
positions[midIndex],
|
|
3428
|
+
positions[midIndex + 1],
|
|
3429
|
+
positions[midIndex + 2]
|
|
3430
|
+
);
|
|
3431
|
+
} else {
|
|
3432
|
+
positionTarget = new THREE2.Vector3().addVectors(
|
|
3433
|
+
hoveredLink.userData.sourceNode.position,
|
|
3434
|
+
hoveredLink.userData.targetNode.position
|
|
3435
|
+
).multiplyScalar(0.5);
|
|
3436
|
+
}
|
|
3437
|
+
isLink = true;
|
|
3438
|
+
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3439
|
+
content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
|
|
3440
|
+
}
|
|
3172
3441
|
}
|
|
3173
3442
|
}
|
|
3174
3443
|
if (positionTarget) {
|
|
@@ -3192,9 +3461,11 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3192
3461
|
const tooltipRect = tooltipEl.getBoundingClientRect();
|
|
3193
3462
|
const offsetX = 20;
|
|
3194
3463
|
const offsetY = 20;
|
|
3195
|
-
if (x + tooltipRect.width + offsetX > clientWidth)
|
|
3464
|
+
if (x + tooltipRect.width + offsetX > clientWidth)
|
|
3465
|
+
x = x - tooltipRect.width - offsetX;
|
|
3196
3466
|
else x = x + offsetX;
|
|
3197
|
-
if (y + tooltipRect.height + offsetY > clientHeight)
|
|
3467
|
+
if (y + tooltipRect.height + offsetY > clientHeight)
|
|
3468
|
+
y = y - tooltipRect.height - offsetY;
|
|
3198
3469
|
else y = y + offsetY;
|
|
3199
3470
|
tooltipEl.style.display = "block";
|
|
3200
3471
|
tooltipEl.style.left = `${x}px`;
|
|
@@ -3228,7 +3499,9 @@ var processDescriptionForSave = (text, existingSections = []) => {
|
|
|
3228
3499
|
const content = parts[i + 2] || "";
|
|
3229
3500
|
let finalUuid = null;
|
|
3230
3501
|
if (suffix) {
|
|
3231
|
-
const existingMatch = existingSections.find(
|
|
3502
|
+
const existingMatch = existingSections.find(
|
|
3503
|
+
(s) => s.id && s.id.includes(suffix)
|
|
3504
|
+
);
|
|
3232
3505
|
if (existingMatch) {
|
|
3233
3506
|
finalUuid = existingMatch.id;
|
|
3234
3507
|
} else {
|
|
@@ -3319,28 +3592,34 @@ var extractFileUrlsFromProperties = (dataObject) => {
|
|
|
3319
3592
|
function useResizablePanel({ initialWidth, minWidth, maxWidth }) {
|
|
3320
3593
|
const [width, setWidth] = (0, import_react3.useState)(initialWidth);
|
|
3321
3594
|
const [isResizing, setIsResizing] = (0, import_react3.useState)(false);
|
|
3322
|
-
const handlePointerDown = (0, import_react3.useCallback)(
|
|
3323
|
-
e
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
const
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3595
|
+
const handlePointerDown = (0, import_react3.useCallback)(
|
|
3596
|
+
(e) => {
|
|
3597
|
+
e.preventDefault();
|
|
3598
|
+
e.stopPropagation();
|
|
3599
|
+
setIsResizing(true);
|
|
3600
|
+
const startX = e.clientX;
|
|
3601
|
+
const startWidth = width;
|
|
3602
|
+
const originalUserSelect = document.body.style.userSelect;
|
|
3603
|
+
document.body.style.userSelect = "none";
|
|
3604
|
+
const handlePointerMove = (moveEvent) => {
|
|
3605
|
+
const deltaX = startX - moveEvent.clientX;
|
|
3606
|
+
const newWidth = Math.min(
|
|
3607
|
+
Math.max(startWidth + deltaX, minWidth),
|
|
3608
|
+
maxWidth
|
|
3609
|
+
);
|
|
3610
|
+
setWidth(newWidth);
|
|
3611
|
+
};
|
|
3612
|
+
const handlePointerUp = () => {
|
|
3613
|
+
setIsResizing(false);
|
|
3614
|
+
document.body.style.userSelect = originalUserSelect;
|
|
3615
|
+
document.removeEventListener("pointermove", handlePointerMove);
|
|
3616
|
+
document.removeEventListener("pointerup", handlePointerUp);
|
|
3617
|
+
};
|
|
3618
|
+
document.addEventListener("pointermove", handlePointerMove);
|
|
3619
|
+
document.addEventListener("pointerup", handlePointerUp);
|
|
3620
|
+
},
|
|
3621
|
+
[width, minWidth, maxWidth]
|
|
3622
|
+
);
|
|
3344
3623
|
return { width, isResizing, handlePointerDown, setWidth };
|
|
3345
3624
|
}
|
|
3346
3625
|
|
|
@@ -3477,9 +3756,9 @@ function CustomPropertyDisplay({
|
|
|
3477
3756
|
};
|
|
3478
3757
|
const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
|
|
3479
3758
|
const handleListItemChange = (j, f, v) => setTempProp((p) => {
|
|
3480
|
-
const
|
|
3481
|
-
|
|
3482
|
-
return { ...p, value:
|
|
3759
|
+
const newValue2 = [...p.value];
|
|
3760
|
+
newValue2[j] = { ...newValue2[j], [f]: v };
|
|
3761
|
+
return { ...p, value: newValue2 };
|
|
3483
3762
|
});
|
|
3484
3763
|
const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
|
|
3485
3764
|
const renderEditView = () => {
|
|
@@ -3533,14 +3812,14 @@ function CustomPropertyDisplay({
|
|
|
3533
3812
|
const inputClass = `${baseInput} ${noSpinnerClass}`;
|
|
3534
3813
|
const handleDateTypeChange = (newDateType) => {
|
|
3535
3814
|
var _a3, _b2, _c2;
|
|
3536
|
-
let
|
|
3815
|
+
let newValue2 = { type: newDateType };
|
|
3537
3816
|
if (newDateType === "Date Interval") {
|
|
3538
|
-
|
|
3539
|
-
|
|
3817
|
+
newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
|
|
3818
|
+
newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
|
|
3540
3819
|
} else {
|
|
3541
|
-
|
|
3820
|
+
newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
|
|
3542
3821
|
}
|
|
3543
|
-
handlePropChange("value",
|
|
3822
|
+
handlePropChange("value", newValue2);
|
|
3544
3823
|
};
|
|
3545
3824
|
const handleDateValueChange = (dateField, dateValue) => {
|
|
3546
3825
|
handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
|
|
@@ -4388,7 +4667,7 @@ ${space}${bullet} `);
|
|
|
4388
4667
|
}
|
|
4389
4668
|
),
|
|
4390
4669
|
/* @__PURE__ */ import_react6.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0 shrink-0" }),
|
|
4391
|
-
/* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.createElement("button", { onClick: handleClose, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
|
|
4670
|
+
/* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-center justify-between gap-4 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ import_react6.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shrink-0" }), /* @__PURE__ */ import_react6.default.createElement("p", { className: "text-sm font-medium text-slate-200 truncate" }, title || "Editar Descri\xE7\xE3o")), /* @__PURE__ */ import_react6.default.createElement("button", { onClick: handleClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")),
|
|
4392
4671
|
/* @__PURE__ */ import_react6.default.createElement("div", { className: "px-6 py-3 flex flex-col gap-3 border-b border-white/5 bg-white/5 shrink-0" }, /* @__PURE__ */ import_react6.default.createElement("div", { className: "flex items-center gap-2 flex-wrap" }, /* @__PURE__ */ import_react6.default.createElement(
|
|
4393
4672
|
"button",
|
|
4394
4673
|
{
|
|
@@ -5503,7 +5782,7 @@ function AncestryRelationshipPanel({
|
|
|
5503
5782
|
onImageClick: handleImageClickFromText,
|
|
5504
5783
|
onSaveDescription: handleSaveDescriptionInline
|
|
5505
5784
|
}
|
|
5506
|
-
) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div",
|
|
5785
|
+
) : /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-cyan-400/0 via-cyan-400/70 to-cyan-400/0" }), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react9.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-cyan-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ import_react9.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o de Ancestralidade")), /* @__PURE__ */ import_react9.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, "Editar Rela\xE7\xE3o")), /* @__PURE__ */ import_react9.default.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react9.default.createElement("div", { className: "px-6 pb-4 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, /* @__PURE__ */ import_react9.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react9.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o da Rela\xE7\xE3o"), /* @__PURE__ */ import_react9.default.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react9.default.createElement(
|
|
5507
5786
|
DescriptionDisplay,
|
|
5508
5787
|
{
|
|
5509
5788
|
description,
|
|
@@ -5808,6 +6087,7 @@ function CreateAncestryPanel({
|
|
|
5808
6087
|
} = ancestryMode;
|
|
5809
6088
|
const [isSaving, setIsSaving] = (0, import_react11.useState)(false);
|
|
5810
6089
|
const [isLinkCopied, setIsLinkCopied] = (0, import_react11.useState)(false);
|
|
6090
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react11.useState)(false);
|
|
5811
6091
|
const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = (0, import_react11.useState)(false);
|
|
5812
6092
|
const handleCopyLink = (e) => {
|
|
5813
6093
|
e.stopPropagation();
|
|
@@ -5875,12 +6155,14 @@ function CreateAncestryPanel({
|
|
|
5875
6155
|
};
|
|
5876
6156
|
const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
|
|
5877
6157
|
setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
|
|
6158
|
+
setHasUnsavedChanges(true);
|
|
5878
6159
|
};
|
|
5879
6160
|
const handleToggleAddMode = (isAbstraction = false) => {
|
|
5880
6161
|
if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
|
|
5881
6162
|
setTargetRenderNodeId(null);
|
|
5882
6163
|
}
|
|
5883
6164
|
setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
|
|
6165
|
+
setHasUnsavedChanges(true);
|
|
5884
6166
|
};
|
|
5885
6167
|
const handleRemoveNode = (0, import_react11.useCallback)((pathToRemove, isAbstraction = false) => {
|
|
5886
6168
|
if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
|
|
@@ -5898,6 +6180,7 @@ function CreateAncestryPanel({
|
|
|
5898
6180
|
const indexToRemove = pathToRemove[pathToRemove.length - 1];
|
|
5899
6181
|
if (currentParent.children && currentParent.children.length > indexToRemove) {
|
|
5900
6182
|
currentParent.children.splice(indexToRemove, 1);
|
|
6183
|
+
setHasUnsavedChanges(true);
|
|
5901
6184
|
}
|
|
5902
6185
|
return { ...prev, [treeKey]: newTree };
|
|
5903
6186
|
});
|
|
@@ -5956,6 +6239,7 @@ function CreateAncestryPanel({
|
|
|
5956
6239
|
updateGlobalTree(rootTreeClone);
|
|
5957
6240
|
}
|
|
5958
6241
|
setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
|
|
6242
|
+
setHasUnsavedChanges(true);
|
|
5959
6243
|
} else {
|
|
5960
6244
|
alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
|
|
5961
6245
|
}
|
|
@@ -6028,6 +6312,7 @@ function CreateAncestryPanel({
|
|
|
6028
6312
|
const handleAddProp = () => {
|
|
6029
6313
|
const newProp = createNewCustomProperty(customProps);
|
|
6030
6314
|
setCustomProps((p) => [...p, newProp]);
|
|
6315
|
+
setHasUnsavedChanges(true);
|
|
6031
6316
|
setTimeout(() => {
|
|
6032
6317
|
var _a;
|
|
6033
6318
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -6036,11 +6321,13 @@ function CreateAncestryPanel({
|
|
|
6036
6321
|
const handleRemoveProp = (i) => {
|
|
6037
6322
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
6038
6323
|
setCustomProps(newProps);
|
|
6324
|
+
setHasUnsavedChanges(true);
|
|
6039
6325
|
};
|
|
6040
6326
|
const handleUpdateProp = (index, updatedProp) => {
|
|
6041
6327
|
const newProps = [...customProps];
|
|
6042
6328
|
newProps[index] = updatedProp;
|
|
6043
6329
|
setCustomProps(newProps);
|
|
6330
|
+
setHasUnsavedChanges(true);
|
|
6044
6331
|
};
|
|
6045
6332
|
const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
|
|
6046
6333
|
(0, import_react11.useEffect)(() => {
|
|
@@ -6150,6 +6437,7 @@ function CreateAncestryPanel({
|
|
|
6150
6437
|
updateGlobalTree(rootTreeClone);
|
|
6151
6438
|
setBranchStack([...branchStack]);
|
|
6152
6439
|
setIsPickerOpen(false);
|
|
6440
|
+
setHasUnsavedChanges(true);
|
|
6153
6441
|
try {
|
|
6154
6442
|
setIsSaving(true);
|
|
6155
6443
|
const rootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6163,6 +6451,7 @@ function CreateAncestryPanel({
|
|
|
6163
6451
|
rootExtras
|
|
6164
6452
|
);
|
|
6165
6453
|
setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
|
|
6454
|
+
setHasUnsavedChanges(false);
|
|
6166
6455
|
if (onRenderFullAncestry) {
|
|
6167
6456
|
const fullTreePayload = {
|
|
6168
6457
|
ancestry_id: ancestryMode.currentAncestryId || "temp_root",
|
|
@@ -6205,6 +6494,7 @@ function CreateAncestryPanel({
|
|
|
6205
6494
|
if (branchIndex !== -1) {
|
|
6206
6495
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6207
6496
|
updateGlobalTree(rootTreeClone);
|
|
6497
|
+
setHasUnsavedChanges(true);
|
|
6208
6498
|
try {
|
|
6209
6499
|
setIsSaving(true);
|
|
6210
6500
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6226,6 +6516,7 @@ function CreateAncestryPanel({
|
|
|
6226
6516
|
isPrivate,
|
|
6227
6517
|
ancestryMode.abstraction_tree
|
|
6228
6518
|
));
|
|
6519
|
+
setHasUnsavedChanges(false);
|
|
6229
6520
|
if (onClearAncestryVisuals) {
|
|
6230
6521
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6231
6522
|
}
|
|
@@ -6258,6 +6549,7 @@ function CreateAncestryPanel({
|
|
|
6258
6549
|
if (branchIndex !== -1) {
|
|
6259
6550
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6260
6551
|
updateGlobalTree(rootTreeClone);
|
|
6552
|
+
setHasUnsavedChanges(true);
|
|
6261
6553
|
try {
|
|
6262
6554
|
setIsSaving(true);
|
|
6263
6555
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6279,6 +6571,7 @@ function CreateAncestryPanel({
|
|
|
6279
6571
|
isPrivate,
|
|
6280
6572
|
ancestryMode.abstraction_tree
|
|
6281
6573
|
));
|
|
6574
|
+
setHasUnsavedChanges(false);
|
|
6282
6575
|
if (onClearAncestryVisuals) {
|
|
6283
6576
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6284
6577
|
}
|
|
@@ -6540,6 +6833,7 @@ function CreateAncestryPanel({
|
|
|
6540
6833
|
}
|
|
6541
6834
|
setBranchStack(parentStack);
|
|
6542
6835
|
setTargetScrollSectionId(targetFocusId);
|
|
6836
|
+
setHasUnsavedChanges(true);
|
|
6543
6837
|
if (onRenderFullAncestry) {
|
|
6544
6838
|
const parentStack2 = currentStack;
|
|
6545
6839
|
const rotation = parentStack2.reduce((acc, step) => {
|
|
@@ -6601,7 +6895,6 @@ function CreateAncestryPanel({
|
|
|
6601
6895
|
direction,
|
|
6602
6896
|
tree: {
|
|
6603
6897
|
node: nodeData,
|
|
6604
|
-
node_id: nodeId,
|
|
6605
6898
|
children: [],
|
|
6606
6899
|
relationship: {}
|
|
6607
6900
|
}
|
|
@@ -6623,6 +6916,7 @@ function CreateAncestryPanel({
|
|
|
6623
6916
|
savedMaxIndex: parentIndexToSave,
|
|
6624
6917
|
entryDirection: direction
|
|
6625
6918
|
}]);
|
|
6919
|
+
setHasUnsavedChanges(true);
|
|
6626
6920
|
if (branch && branch.tree && onRenderFullAncestry) {
|
|
6627
6921
|
const branchAncestryObj = {
|
|
6628
6922
|
ancestry_id: branch.id,
|
|
@@ -6673,6 +6967,10 @@ function CreateAncestryPanel({
|
|
|
6673
6967
|
const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
|
|
6674
6968
|
const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
|
|
6675
6969
|
const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
6970
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
6971
|
+
onClose();
|
|
6972
|
+
return;
|
|
6973
|
+
}
|
|
6676
6974
|
if (!currentInputName.trim()) {
|
|
6677
6975
|
alert("O nome n\xE3o pode estar vazio.");
|
|
6678
6976
|
return;
|
|
@@ -6680,11 +6978,7 @@ function CreateAncestryPanel({
|
|
|
6680
6978
|
setIsSaving(true);
|
|
6681
6979
|
const processedSections = processDescriptionForSave(currentInputDesc, currentInputSections);
|
|
6682
6980
|
setExistingSections(processedSections);
|
|
6683
|
-
const updatedRootTree =
|
|
6684
|
-
ancestryMode.tree,
|
|
6685
|
-
currentInputDesc,
|
|
6686
|
-
processedSections
|
|
6687
|
-
);
|
|
6981
|
+
const updatedRootTree = JSON.parse(JSON.stringify(ancestryMode.tree));
|
|
6688
6982
|
const extrasObj = {
|
|
6689
6983
|
...toObjectFromCustomProps(customProps.filter((p) => !p.isEditing)),
|
|
6690
6984
|
is_private: isPrivate
|
|
@@ -6726,6 +7020,7 @@ function CreateAncestryPanel({
|
|
|
6726
7020
|
isPrivate,
|
|
6727
7021
|
ancestryMode.abstraction_tree
|
|
6728
7022
|
));
|
|
7023
|
+
setHasUnsavedChanges(false);
|
|
6729
7024
|
if (onRenderFullAncestry) {
|
|
6730
7025
|
const rotation = branchStack.reduce((acc, step) => {
|
|
6731
7026
|
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
@@ -6777,6 +7072,7 @@ function CreateAncestryPanel({
|
|
|
6777
7072
|
updatedRootTree,
|
|
6778
7073
|
extrasObj
|
|
6779
7074
|
);
|
|
7075
|
+
setHasUnsavedChanges(false);
|
|
6780
7076
|
setLastSavedSnapshot(takeSnapshot(
|
|
6781
7077
|
updatedRootTree,
|
|
6782
7078
|
currentInputName,
|
|
@@ -6799,6 +7095,7 @@ function CreateAncestryPanel({
|
|
|
6799
7095
|
const newTreeString = JSON.stringify(newRootTree);
|
|
6800
7096
|
if (currentTreeString !== newTreeString) {
|
|
6801
7097
|
updateGlobalTree(newRootTree);
|
|
7098
|
+
setHasUnsavedChanges(true);
|
|
6802
7099
|
}
|
|
6803
7100
|
}, [description, existingSections]);
|
|
6804
7101
|
const handleTriggerFullRender = () => {
|
|
@@ -6821,6 +7118,7 @@ function CreateAncestryPanel({
|
|
|
6821
7118
|
};
|
|
6822
7119
|
const handleSaveDescriptionInline = (newDesc) => {
|
|
6823
7120
|
setDescription(newDesc);
|
|
7121
|
+
setHasUnsavedChanges(true);
|
|
6824
7122
|
handleLocalSave(true, { description: newDesc });
|
|
6825
7123
|
};
|
|
6826
7124
|
const swallow = (e) => e.stopPropagation();
|
|
@@ -6950,7 +7248,11 @@ function CreateAncestryPanel({
|
|
|
6950
7248
|
{
|
|
6951
7249
|
type: "text",
|
|
6952
7250
|
value: ancestryName,
|
|
6953
|
-
onChange: (e) =>
|
|
7251
|
+
onChange: (e) => {
|
|
7252
|
+
setAncestryName(e.target.value);
|
|
7253
|
+
setHasUnsavedChanges(true);
|
|
7254
|
+
},
|
|
7255
|
+
readOnly: isContextLinked,
|
|
6954
7256
|
placeholder: "Nome da Ancestralidade",
|
|
6955
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"
|
|
6956
7258
|
}
|
|
@@ -7438,7 +7740,7 @@ function ColorPicker({ color, onChange, disabled }) {
|
|
|
7438
7740
|
style: { backgroundColor: preset },
|
|
7439
7741
|
title: preset
|
|
7440
7742
|
},
|
|
7441
|
-
color.toLowerCase() === preset.toLowerCase() && /* @__PURE__ */ import_react13.default.createElement(import_fi11.FiCheck, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
|
|
7743
|
+
(color || "").toLowerCase() === (preset || "").toLowerCase() && /* @__PURE__ */ import_react13.default.createElement(import_fi11.FiCheck, { className: `drop-shadow-md ${["#ffffff", "#4df5cb", "#84cc16", "#f59e0b"].includes(preset) ? "text-black" : "text-white"}`, size: 12 })
|
|
7442
7744
|
)))), /* @__PURE__ */ import_react13.default.createElement("style", null, `
|
|
7443
7745
|
.custom-react-colorful .react-colorful {
|
|
7444
7746
|
width: 100%;
|
|
@@ -7530,13 +7832,23 @@ function InSceneCreationForm({
|
|
|
7530
7832
|
}, [hasImages, useImageAsTexture, onImageChange]);
|
|
7531
7833
|
(0, import_react14.useEffect)(() => {
|
|
7532
7834
|
let result = [];
|
|
7835
|
+
const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
|
|
7533
7836
|
if (typeInput.trim() === "") {
|
|
7534
|
-
result =
|
|
7837
|
+
result = validExistingTypes.filter((t) => !types.includes(t));
|
|
7535
7838
|
} else {
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
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
|
+
}
|
|
7540
7852
|
}
|
|
7541
7853
|
if (sourceTypes) {
|
|
7542
7854
|
const priorityTypes = Array.isArray(sourceTypes) ? sourceTypes : [sourceTypes];
|
|
@@ -7599,9 +7911,9 @@ function InSceneCreationForm({
|
|
|
7599
7911
|
};
|
|
7600
7912
|
const handleToggleImageMode = () => {
|
|
7601
7913
|
var _a2, _b;
|
|
7602
|
-
const
|
|
7603
|
-
setUseImageAsTexture(
|
|
7604
|
-
if (
|
|
7914
|
+
const newValue2 = !useImageAsTexture;
|
|
7915
|
+
setUseImageAsTexture(newValue2);
|
|
7916
|
+
if (newValue2) {
|
|
7605
7917
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7606
7918
|
if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
|
|
7607
7919
|
const url = firstImageProp.value[0].value;
|
|
@@ -7899,9 +8211,9 @@ function InSceneVersionForm({
|
|
|
7899
8211
|
};
|
|
7900
8212
|
const handleToggleImageMode = () => {
|
|
7901
8213
|
var _a, _b;
|
|
7902
|
-
const
|
|
7903
|
-
setUseImageAsTexture(
|
|
7904
|
-
if (
|
|
8214
|
+
const newValue2 = !useImageAsTexture;
|
|
8215
|
+
setUseImageAsTexture(newValue2);
|
|
8216
|
+
if (newValue2) {
|
|
7905
8217
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7906
8218
|
if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
|
|
7907
8219
|
const url = firstImageProp.value[0].value;
|
|
@@ -8077,10 +8389,13 @@ function InSceneQuestForm({
|
|
|
8077
8389
|
onSizeChange,
|
|
8078
8390
|
viewName = "Projeto",
|
|
8079
8391
|
// NOVA PROP
|
|
8080
|
-
questCounter = 1
|
|
8392
|
+
questCounter = 1,
|
|
8081
8393
|
// NOVA PROP
|
|
8394
|
+
viewMembers = []
|
|
8082
8395
|
}) {
|
|
8396
|
+
var _a, _b;
|
|
8083
8397
|
const [name, setName] = (0, import_react16.useState)("");
|
|
8398
|
+
const [assigneeId, setAssigneeId] = (0, import_react16.useState)("");
|
|
8084
8399
|
const [types, setTypes] = (0, import_react16.useState)(["quest"]);
|
|
8085
8400
|
const [typeInput, setTypeInput] = (0, import_react16.useState)("");
|
|
8086
8401
|
const [status, setStatus] = (0, import_react16.useState)("Backlog");
|
|
@@ -8088,6 +8403,8 @@ function InSceneQuestForm({
|
|
|
8088
8403
|
const [intensity, setIntensity] = (0, import_react16.useState)(0);
|
|
8089
8404
|
const [description, setDescription] = (0, import_react16.useState)("");
|
|
8090
8405
|
const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react16.useState)(false);
|
|
8406
|
+
const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react16.useState)(false);
|
|
8407
|
+
const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react16.useState)("");
|
|
8091
8408
|
const [customProps, setCustomProps] = (0, import_react16.useState)([]);
|
|
8092
8409
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react16.useState)(false);
|
|
8093
8410
|
const propsEndRef = (0, import_react16.useRef)(null);
|
|
@@ -8096,8 +8413,8 @@ function InSceneQuestForm({
|
|
|
8096
8413
|
const newProp = createNewCustomProperty(customProps);
|
|
8097
8414
|
setCustomProps([...customProps, newProp]);
|
|
8098
8415
|
setTimeout(() => {
|
|
8099
|
-
var
|
|
8100
|
-
(
|
|
8416
|
+
var _a2;
|
|
8417
|
+
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
8101
8418
|
}, 100);
|
|
8102
8419
|
};
|
|
8103
8420
|
const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
|
|
@@ -8143,6 +8460,7 @@ function InSceneQuestForm({
|
|
|
8143
8460
|
type: types,
|
|
8144
8461
|
color: QUEST_STATUS_COLORS2[status],
|
|
8145
8462
|
status,
|
|
8463
|
+
assignee_id: assigneeId || null,
|
|
8146
8464
|
size,
|
|
8147
8465
|
intensity,
|
|
8148
8466
|
description: description.trim(),
|
|
@@ -8214,7 +8532,59 @@ function InSceneQuestForm({
|
|
|
8214
8532
|
},
|
|
8215
8533
|
/* @__PURE__ */ import_react16.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS2[s] } }),
|
|
8216
8534
|
s
|
|
8217
|
-
)))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "
|
|
8535
|
+
)))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react16.default.createElement(
|
|
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__ */ import_react16.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react16.default.createElement("span", { className: "text-slate-200 font-medium" }, ((_a = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _a.name) || ((_b = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _b.email) || "Nenhum")),
|
|
8543
|
+
/* @__PURE__ */ import_react16.default.createElement(import_fi14.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
|
|
8544
|
+
), isAssigneeDropdownOpen && /* @__PURE__ */ import_react16.default.createElement(import_react16.default.Fragment, null, /* @__PURE__ */ import_react16.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
|
|
8545
|
+
setIsAssigneeDropdownOpen(false);
|
|
8546
|
+
setAssigneeSearchQuery("");
|
|
8547
|
+
} }), /* @__PURE__ */ import_react16.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react16.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react16.default.createElement(
|
|
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__ */ import_react16.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react16.default.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__ */ import_react16.default.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__ */ import_react16.default.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado")))))), /* @__PURE__ */ import_react16.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react16.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react16.default.createElement("div", { className: "relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 focus-within:ring-2 focus-within:ring-indigo-400/60 transition-all" }, types.map((t, index) => /* @__PURE__ */ import_react16.default.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, t !== "quest" && /* @__PURE__ */ import_react16.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react16.default.createElement(import_fi14.FiX, { size: 12 })))), /* @__PURE__ */ import_react16.default.createElement(
|
|
8218
8588
|
"input",
|
|
8219
8589
|
{
|
|
8220
8590
|
type: "text",
|
|
@@ -8321,6 +8691,7 @@ function NodeDetailsPanel({
|
|
|
8321
8691
|
return !!(node == null ? void 0 : node.useImageAsTexture);
|
|
8322
8692
|
});
|
|
8323
8693
|
const [selectedImageUrl, setSelectedImageUrl] = (0, import_react17.useState)((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8694
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react17.useState)(false);
|
|
8324
8695
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8325
8696
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8326
8697
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8358,6 +8729,7 @@ function NodeDetailsPanel({
|
|
|
8358
8729
|
else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
|
|
8359
8730
|
else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
|
|
8360
8731
|
setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8732
|
+
setHasUnsavedChanges(false);
|
|
8361
8733
|
}
|
|
8362
8734
|
}, [node]);
|
|
8363
8735
|
const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
|
|
@@ -8368,15 +8740,22 @@ function NodeDetailsPanel({
|
|
|
8368
8740
|
}
|
|
8369
8741
|
}, [hasImages, useImageAsTexture]);
|
|
8370
8742
|
(0, import_react17.useEffect)(() => {
|
|
8743
|
+
const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
|
|
8371
8744
|
if (typeInput.trim() === "") {
|
|
8372
|
-
setFilteredTypes(
|
|
8745
|
+
setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
|
|
8373
8746
|
} else {
|
|
8374
|
-
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
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
|
+
}
|
|
8380
8759
|
}
|
|
8381
8760
|
}, [typeInput, existingTypes, types]);
|
|
8382
8761
|
const handleIntensityChangeLocal = (e) => {
|
|
@@ -8384,6 +8763,7 @@ function NodeDetailsPanel({
|
|
|
8384
8763
|
setIntensity(val);
|
|
8385
8764
|
onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
|
|
8386
8765
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
|
|
8766
|
+
setHasUnsavedChanges(true);
|
|
8387
8767
|
};
|
|
8388
8768
|
const handleCopyLink = () => {
|
|
8389
8769
|
if (!(node == null ? void 0 : node.id)) return;
|
|
@@ -8401,14 +8781,17 @@ function NodeDetailsPanel({
|
|
|
8401
8781
|
const v = e.target.value;
|
|
8402
8782
|
setName(v);
|
|
8403
8783
|
onNameChange == null ? void 0 : onNameChange(node.id, v);
|
|
8784
|
+
setHasUnsavedChanges(true);
|
|
8404
8785
|
};
|
|
8405
8786
|
const handleColorChange = (val) => {
|
|
8406
8787
|
setColor(val);
|
|
8407
8788
|
onColorChange == null ? void 0 : onColorChange(node.id, val);
|
|
8789
|
+
setHasUnsavedChanges(true);
|
|
8408
8790
|
};
|
|
8409
8791
|
const handleSizeChange = (newSize) => {
|
|
8410
8792
|
setSize(newSize);
|
|
8411
8793
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8794
|
+
setHasUnsavedChanges(true);
|
|
8412
8795
|
};
|
|
8413
8796
|
const handleAddType = (newType) => {
|
|
8414
8797
|
const trimmed = newType.trim();
|
|
@@ -8416,10 +8799,12 @@ function NodeDetailsPanel({
|
|
|
8416
8799
|
setTypes([...types, trimmed]);
|
|
8417
8800
|
setTypeInput("");
|
|
8418
8801
|
setShowTypeSuggestions(false);
|
|
8802
|
+
setHasUnsavedChanges(true);
|
|
8419
8803
|
}
|
|
8420
8804
|
};
|
|
8421
8805
|
const handleRemoveType = (indexToRemove) => {
|
|
8422
8806
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
8807
|
+
setHasUnsavedChanges(true);
|
|
8423
8808
|
};
|
|
8424
8809
|
const handleTypeInputKeyDown = (e) => {
|
|
8425
8810
|
if (e.key === "Enter") {
|
|
@@ -8432,6 +8817,7 @@ function NodeDetailsPanel({
|
|
|
8432
8817
|
const handleAddProp = () => {
|
|
8433
8818
|
const newProp = createNewCustomProperty(customProps);
|
|
8434
8819
|
setCustomProps((p) => [...p, newProp]);
|
|
8820
|
+
setHasUnsavedChanges(true);
|
|
8435
8821
|
setTimeout(() => {
|
|
8436
8822
|
var _a;
|
|
8437
8823
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8440,19 +8826,21 @@ function NodeDetailsPanel({
|
|
|
8440
8826
|
const handleRemoveProp = (i) => {
|
|
8441
8827
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8442
8828
|
setCustomProps(newProps);
|
|
8829
|
+
setHasUnsavedChanges(true);
|
|
8443
8830
|
triggerAutoSave({ customProps: newProps });
|
|
8444
8831
|
};
|
|
8445
8832
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8446
8833
|
const newProps = [...customProps];
|
|
8447
8834
|
newProps[index] = updatedProp;
|
|
8448
8835
|
setCustomProps(newProps);
|
|
8836
|
+
setHasUnsavedChanges(true);
|
|
8449
8837
|
if (!updatedProp.isEditing) {
|
|
8450
8838
|
triggerAutoSave({ customProps: newProps });
|
|
8451
8839
|
}
|
|
8452
8840
|
};
|
|
8453
8841
|
const handleToggleImageMode = () => {
|
|
8454
|
-
const newValue = !useImageAsTexture;
|
|
8455
8842
|
setUseImageAsTexture(newValue);
|
|
8843
|
+
setHasUnsavedChanges(true);
|
|
8456
8844
|
let activeUrl = null;
|
|
8457
8845
|
if (newValue) {
|
|
8458
8846
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
@@ -8474,6 +8862,7 @@ function NodeDetailsPanel({
|
|
|
8474
8862
|
};
|
|
8475
8863
|
const handleSelectTexture = (url) => {
|
|
8476
8864
|
setSelectedImageUrl(url);
|
|
8865
|
+
setHasUnsavedChanges(true);
|
|
8477
8866
|
onImageChange == null ? void 0 : onImageChange(true, url, color);
|
|
8478
8867
|
onDataUpdate == null ? void 0 : onDataUpdate({
|
|
8479
8868
|
...node,
|
|
@@ -8484,6 +8873,7 @@ function NodeDetailsPanel({
|
|
|
8484
8873
|
};
|
|
8485
8874
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8486
8875
|
setDescription(newDescription);
|
|
8876
|
+
setHasUnsavedChanges(true);
|
|
8487
8877
|
onDataUpdate({ ...node, description: newDescription });
|
|
8488
8878
|
triggerAutoSave({ description: newDescription });
|
|
8489
8879
|
};
|
|
@@ -8494,6 +8884,10 @@ function NodeDetailsPanel({
|
|
|
8494
8884
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8495
8885
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8496
8886
|
const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
|
|
8887
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
8888
|
+
onClose();
|
|
8889
|
+
return;
|
|
8890
|
+
}
|
|
8497
8891
|
if (!currentName.trim() || currentTypes.length === 0) {
|
|
8498
8892
|
alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8499
8893
|
return;
|
|
@@ -8518,6 +8912,7 @@ function NodeDetailsPanel({
|
|
|
8518
8912
|
};
|
|
8519
8913
|
await onSave(dataToSave, keepOpen);
|
|
8520
8914
|
onDataUpdate(dataToSave);
|
|
8915
|
+
setHasUnsavedChanges(false);
|
|
8521
8916
|
if (!keepOpen) {
|
|
8522
8917
|
onClose();
|
|
8523
8918
|
}
|
|
@@ -8584,7 +8979,7 @@ function NodeDetailsPanel({
|
|
|
8584
8979
|
onImageClick: handleImageClickFromText,
|
|
8585
8980
|
onSaveDescription: handleSaveDescriptionInline
|
|
8586
8981
|
}
|
|
8587
|
-
) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div",
|
|
8982
|
+
) : /* @__PURE__ */ import_react17.default.createElement(import_react17.default.Fragment, null, /* @__PURE__ */ import_react17.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-indigo-400/0 via-indigo-400/70 to-indigo-400/0" }), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react17.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-indigo-400/80 shadow-[0_0_18px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ import_react17.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes do Node"), /* @__PURE__ */ import_react17.default.createElement(
|
|
8588
8983
|
"button",
|
|
8589
8984
|
{
|
|
8590
8985
|
onClick: handleCopyLink,
|
|
@@ -8592,7 +8987,7 @@ function NodeDetailsPanel({
|
|
|
8592
8987
|
title: isLinkCopied ? "Link Copiado!" : "Copiar link para este Node"
|
|
8593
8988
|
},
|
|
8594
8989
|
isLinkCopied ? /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react17.default.createElement(import_fi15.FiLink, { size: 12 })
|
|
8595
|
-
)), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.default.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ import_react17.default.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ import_react17.default.createElement(
|
|
8990
|
+
)), /* @__PURE__ */ import_react17.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react17.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ import_react17.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react17.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react17.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos"), /* @__PURE__ */ import_react17.default.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ import_react17.default.createElement("span", { key: index, className: "flex items-center gap-1 bg-indigo-500/30 text-indigo-100 px-1.5 py-0.5 rounded-md text-xs font-medium border border-indigo-500/20" }, t, canEdit && /* @__PURE__ */ import_react17.default.createElement(
|
|
8596
8991
|
"button",
|
|
8597
8992
|
{
|
|
8598
8993
|
type: "button",
|
|
@@ -8754,6 +9149,7 @@ function NodeDetailsPanel({
|
|
|
8754
9149
|
initialValue: description,
|
|
8755
9150
|
onSave: (newDescription) => {
|
|
8756
9151
|
setDescription(newDescription);
|
|
9152
|
+
setHasUnsavedChanges(true);
|
|
8757
9153
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
8758
9154
|
triggerAutoSave({ description: newDescription });
|
|
8759
9155
|
},
|
|
@@ -8790,7 +9186,8 @@ function QuestDetailsPanel({
|
|
|
8790
9186
|
onMentionClick,
|
|
8791
9187
|
onUploadFile,
|
|
8792
9188
|
userRole,
|
|
8793
|
-
currentDatasetName
|
|
9189
|
+
currentDatasetName,
|
|
9190
|
+
viewMembers = []
|
|
8794
9191
|
}) {
|
|
8795
9192
|
var _a;
|
|
8796
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) || "";
|
|
@@ -8802,9 +9199,12 @@ function QuestDetailsPanel({
|
|
|
8802
9199
|
const [typeInput, setTypeInput] = (0, import_react18.useState)("");
|
|
8803
9200
|
const [status, setStatus] = (0, import_react18.useState)((node == null ? void 0 : node.status) ?? "Backlog");
|
|
8804
9201
|
const [size, setSize] = (0, import_react18.useState)((node == null ? void 0 : node.size) ?? "medium");
|
|
9202
|
+
const [assigneeId, setAssigneeId] = (0, import_react18.useState)((node == null ? void 0 : node.assignee_id) || "");
|
|
8805
9203
|
const [description, setDescription] = (0, import_react18.useState)((node == null ? void 0 : node.description) ?? "");
|
|
8806
9204
|
const [intensity, setIntensity] = (0, import_react18.useState)((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8807
9205
|
const [isStatusDropdownOpen, setIsStatusDropdownOpen] = (0, import_react18.useState)(false);
|
|
9206
|
+
const [isAssigneeDropdownOpen, setIsAssigneeDropdownOpen] = (0, import_react18.useState)(false);
|
|
9207
|
+
const [assigneeSearchQuery, setAssigneeSearchQuery] = (0, import_react18.useState)("");
|
|
8808
9208
|
const [customProps, setCustomProps] = (0, import_react18.useState)(() => extractCustomPropsFromNode(node || {}));
|
|
8809
9209
|
const [showTypeSuggestions, setShowTypeSuggestions] = (0, import_react18.useState)(false);
|
|
8810
9210
|
const [filteredTypes, setFilteredTypes] = (0, import_react18.useState)([]);
|
|
@@ -8813,6 +9213,7 @@ function QuestDetailsPanel({
|
|
|
8813
9213
|
const [existingSections, setExistingSections] = (0, import_react18.useState)((node == null ? void 0 : node.description_sections) || []);
|
|
8814
9214
|
const [isSaving, setIsSaving] = (0, import_react18.useState)(false);
|
|
8815
9215
|
const [isLinkCopied, setIsLinkCopied] = (0, import_react18.useState)(false);
|
|
9216
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react18.useState)(false);
|
|
8816
9217
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8817
9218
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8818
9219
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8840,22 +9241,34 @@ function QuestDetailsPanel({
|
|
|
8840
9241
|
setTypes((node == null ? void 0 : node.type) ? Array.isArray(node.type) ? node.type : [node.type] : ["quest"]);
|
|
8841
9242
|
setStatus((node == null ? void 0 : node.status) ?? "Backlog");
|
|
8842
9243
|
setSize((node == null ? void 0 : node.size) ?? "medium");
|
|
9244
|
+
setAssigneeId((node == null ? void 0 : node.assignee_id) || "");
|
|
8843
9245
|
setDescription((node == null ? void 0 : node.description) ?? "");
|
|
8844
9246
|
setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8845
9247
|
setExistingSections((node == null ? void 0 : node.description_sections) || []);
|
|
8846
9248
|
setCustomProps(extractCustomPropsFromNode(node || {}));
|
|
9249
|
+
setHasUnsavedChanges(false);
|
|
8847
9250
|
}
|
|
8848
9251
|
}, [node]);
|
|
8849
9252
|
(0, import_react18.useEffect)(() => {
|
|
9253
|
+
const validExistingTypes = existingTypes.filter((t) => t && typeof t === "string" && t.trim() !== "");
|
|
8850
9254
|
if (typeInput.trim() === "") {
|
|
8851
|
-
setFilteredTypes(
|
|
9255
|
+
setFilteredTypes(validExistingTypes.filter((t) => !types.includes(t)));
|
|
8852
9256
|
} else {
|
|
8853
|
-
|
|
8854
|
-
|
|
8855
|
-
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
|
|
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
|
+
}
|
|
8859
9272
|
}
|
|
8860
9273
|
}, [typeInput, existingTypes, types]);
|
|
8861
9274
|
const handleCopyLink = () => {
|
|
@@ -8875,16 +9288,19 @@ function QuestDetailsPanel({
|
|
|
8875
9288
|
setRawTitle(val);
|
|
8876
9289
|
const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
|
|
8877
9290
|
onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
|
|
9291
|
+
setHasUnsavedChanges(true);
|
|
8878
9292
|
};
|
|
8879
9293
|
const handleSizeChange = (newSize) => {
|
|
8880
9294
|
setSize(newSize);
|
|
8881
9295
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
9296
|
+
setHasUnsavedChanges(true);
|
|
8882
9297
|
};
|
|
8883
9298
|
const handleStatusChange = (newStatus) => {
|
|
8884
9299
|
setStatus(newStatus);
|
|
8885
9300
|
const newColor = QUEST_STATUS_COLORS3[newStatus];
|
|
8886
9301
|
onColorChange == null ? void 0 : onColorChange(node.id, newColor);
|
|
8887
9302
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
|
|
9303
|
+
setHasUnsavedChanges(true);
|
|
8888
9304
|
};
|
|
8889
9305
|
const handleAddType = (newType) => {
|
|
8890
9306
|
const trimmed = newType.trim();
|
|
@@ -8892,11 +9308,13 @@ function QuestDetailsPanel({
|
|
|
8892
9308
|
setTypes([...types, trimmed]);
|
|
8893
9309
|
setTypeInput("");
|
|
8894
9310
|
setShowTypeSuggestions(false);
|
|
9311
|
+
setHasUnsavedChanges(true);
|
|
8895
9312
|
}
|
|
8896
9313
|
};
|
|
8897
9314
|
const handleRemoveType = (indexToRemove) => {
|
|
8898
9315
|
if (types[indexToRemove] === "quest") return;
|
|
8899
9316
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
9317
|
+
setHasUnsavedChanges(true);
|
|
8900
9318
|
};
|
|
8901
9319
|
const handleTypeInputKeyDown = (e) => {
|
|
8902
9320
|
if (e.key === "Enter") {
|
|
@@ -8909,6 +9327,7 @@ function QuestDetailsPanel({
|
|
|
8909
9327
|
const handleAddProp = () => {
|
|
8910
9328
|
const newProp = createNewCustomProperty(customProps);
|
|
8911
9329
|
setCustomProps((p) => [...p, newProp]);
|
|
9330
|
+
setHasUnsavedChanges(true);
|
|
8912
9331
|
setTimeout(() => {
|
|
8913
9332
|
var _a2;
|
|
8914
9333
|
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8917,18 +9336,21 @@ function QuestDetailsPanel({
|
|
|
8917
9336
|
const handleRemoveProp = (i) => {
|
|
8918
9337
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8919
9338
|
setCustomProps(newProps);
|
|
9339
|
+
setHasUnsavedChanges(true);
|
|
8920
9340
|
triggerAutoSave({ customProps: newProps });
|
|
8921
9341
|
};
|
|
8922
9342
|
const handleUpdateProp = (index, updatedProp) => {
|
|
8923
9343
|
const newProps = [...customProps];
|
|
8924
9344
|
newProps[index] = updatedProp;
|
|
8925
9345
|
setCustomProps(newProps);
|
|
9346
|
+
setHasUnsavedChanges(true);
|
|
8926
9347
|
if (!updatedProp.isEditing) {
|
|
8927
9348
|
triggerAutoSave({ customProps: newProps });
|
|
8928
9349
|
}
|
|
8929
9350
|
};
|
|
8930
9351
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8931
9352
|
setDescription(newDescription);
|
|
9353
|
+
setHasUnsavedChanges(true);
|
|
8932
9354
|
onDataUpdate({ ...node, description: newDescription });
|
|
8933
9355
|
triggerAutoSave({ description: newDescription });
|
|
8934
9356
|
};
|
|
@@ -8936,10 +9358,15 @@ function QuestDetailsPanel({
|
|
|
8936
9358
|
const currentRawTitle = overrides.rawTitle !== void 0 ? overrides.rawTitle : rawTitle;
|
|
8937
9359
|
const currentStandardName = questPrefix ? `${questPrefix} - \xBB ${currentRawTitle || "Sem t\xEDtulo"}` : currentRawTitle;
|
|
8938
9360
|
const currentTypes = overrides.types !== void 0 ? overrides.types : types;
|
|
9361
|
+
const currentAssigneeId = overrides.assigneeId !== void 0 ? overrides.assigneeId : assigneeId;
|
|
8939
9362
|
const currentDescription = overrides.description !== void 0 ? overrides.description : description;
|
|
8940
9363
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8941
9364
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8942
9365
|
const currentStatus = overrides.status !== void 0 ? overrides.status : status;
|
|
9366
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9367
|
+
onClose();
|
|
9368
|
+
return;
|
|
9369
|
+
}
|
|
8943
9370
|
if (!currentRawTitle.trim() || currentTypes.length === 0) {
|
|
8944
9371
|
alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8945
9372
|
return;
|
|
@@ -8957,6 +9384,7 @@ function QuestDetailsPanel({
|
|
|
8957
9384
|
type: currentTypes,
|
|
8958
9385
|
color: QUEST_STATUS_COLORS3[currentStatus],
|
|
8959
9386
|
status: currentStatus,
|
|
9387
|
+
assignee_id: currentAssigneeId || null,
|
|
8960
9388
|
size,
|
|
8961
9389
|
description: currentDescription,
|
|
8962
9390
|
description_sections: processedSections,
|
|
@@ -8969,6 +9397,7 @@ function QuestDetailsPanel({
|
|
|
8969
9397
|
};
|
|
8970
9398
|
await onSave(dataToSave, keepOpen);
|
|
8971
9399
|
onDataUpdate(dataToSave);
|
|
9400
|
+
setHasUnsavedChanges(false);
|
|
8972
9401
|
if (!keepOpen) {
|
|
8973
9402
|
onClose();
|
|
8974
9403
|
}
|
|
@@ -8988,6 +9417,8 @@ function QuestDetailsPanel({
|
|
|
8988
9417
|
onClose();
|
|
8989
9418
|
};
|
|
8990
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;
|
|
8991
9422
|
return /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement(
|
|
8992
9423
|
"div",
|
|
8993
9424
|
{
|
|
@@ -9026,7 +9457,7 @@ function QuestDetailsPanel({
|
|
|
9026
9457
|
onImageClick: handleImageClickFromText,
|
|
9027
9458
|
onSaveDescription: handleSaveDescriptionInline
|
|
9028
9459
|
}
|
|
9029
|
-
) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div",
|
|
9460
|
+
) : /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "h-[2px]", style: { background: `linear-gradient(to right, transparent, ${QUEST_STATUS_COLORS3[status]}, transparent)` } }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiTarget, { className: "text-sky-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Quest"), /* @__PURE__ */ import_react18.default.createElement("button", { onClick: handleCopyLink, className: `ml-1 p-1 transition-colors ${isLinkCopied ? "text-green-400" : "text-slate-400 hover:text-sky-400"}`, title: isLinkCopied ? "Link Copiado!" : "Copiar link para esta Quest" }, isLinkCopied ? /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiCheck, { size: 12 }) : /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiLink, { size: 12 }))), /* @__PURE__ */ import_react18.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, standardizedName || (node == null ? void 0 : node.name))), /* @__PURE__ */ import_react18.default.createElement("button", { onClick: handleCancel, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Cancelar" }, "\xD7")), /* @__PURE__ */ import_react18.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "T\xEDtulo da Quest"), /* @__PURE__ */ import_react18.default.createElement(
|
|
9030
9461
|
"input",
|
|
9031
9462
|
{
|
|
9032
9463
|
type: "text",
|
|
@@ -9057,7 +9488,71 @@ function QuestDetailsPanel({
|
|
|
9057
9488
|
},
|
|
9058
9489
|
/* @__PURE__ */ import_react18.default.createElement("span", { className: "w-3 h-3 rounded-full", style: { backgroundColor: QUEST_STATUS_COLORS3[s] } }),
|
|
9059
9490
|
s
|
|
9060
|
-
)))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "
|
|
9491
|
+
)))))), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5 relative mt-2" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Assignee (Respons\xE1vel)"), canEdit ? /* @__PURE__ */ import_react18.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react18.default.createElement(
|
|
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__ */ import_react18.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiUser, { className: "text-slate-400", size: 14 }), /* @__PURE__ */ import_react18.default.createElement("span", { className: "text-slate-200 font-medium" }, isAssigneeUndefined ? "Undefined" : (assigneeMember == null ? void 0 : assigneeMember.name) || (assigneeMember == null ? void 0 : assigneeMember.email) || "Nenhum")),
|
|
9499
|
+
/* @__PURE__ */ import_react18.default.createElement(import_fi16.FiChevronDown, { className: `text-slate-400 transition-transform duration-200 ${isAssigneeDropdownOpen ? "rotate-180" : ""}` })
|
|
9500
|
+
), isAssigneeDropdownOpen && /* @__PURE__ */ import_react18.default.createElement(import_react18.default.Fragment, null, /* @__PURE__ */ import_react18.default.createElement("div", { className: "fixed inset-0 z-40", onClick: () => {
|
|
9501
|
+
setIsAssigneeDropdownOpen(false);
|
|
9502
|
+
setAssigneeSearchQuery("");
|
|
9503
|
+
} }), /* @__PURE__ */ import_react18.default.createElement("div", { className: "absolute top-full left-0 mt-1.5 w-full bg-slate-900 border border-white/10 rounded-lg shadow-[0_8px_30px_rgba(0,0,0,0.5)] z-50 overflow-hidden flex flex-col" }, /* @__PURE__ */ import_react18.default.createElement("div", { className: "p-2 border-b border-white/5 bg-white/5 flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiSearch, { className: "text-slate-500", size: 14 }), /* @__PURE__ */ import_react18.default.createElement(
|
|
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__ */ import_react18.default.createElement("ul", { className: "max-h-48 overflow-y-auto custom-scrollbar" }, /* @__PURE__ */ import_react18.default.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__ */ import_react18.default.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__ */ import_react18.default.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__ */ import_react18.default.createElement("li", { className: "px-3 py-4 text-xs text-slate-500 text-center italic" }, "Nenhum membro encontrado"))))) : /* @__PURE__ */ import_react18.default.createElement("div", { className: "w-full bg-slate-800/70 p-2.5 text-sm rounded-lg border border-white/10 text-slate-400 flex items-center gap-2" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiUser, { className: "opacity-50", size: 14 }), assigneeId ? assigneeMember ? assigneeMember.name || assigneeMember.email : "Undefined" : "Nenhum")), /* @__PURE__ */ import_react18.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react18.default.createElement("label", { className: "text-xs text-slate-300" }, "Tipos Adicionais"), /* @__PURE__ */ import_react18.default.createElement("div", { className: `relative w-full bg-slate-800/70 p-1.5 min-h-[42px] flex flex-wrap gap-1.5 rounded-lg border border-white/10 ${canEdit ? "focus-within:ring-2 focus-within:ring-indigo-400/60" : ""} transition-all` }, types.map((t, index) => /* @__PURE__ */ import_react18.default.createElement("span", { key: index, className: `flex items-center gap-1 px-1.5 py-0.5 rounded-md text-xs font-medium border ${t === "quest" ? "bg-sky-500/20 text-sky-200 border-sky-500/30" : "bg-indigo-500/30 text-indigo-100 border-indigo-500/20"}` }, t, canEdit && t !== "quest" && /* @__PURE__ */ import_react18.default.createElement("button", { type: "button", onClick: () => handleRemoveType(index), className: "hover:text-white transition-colors" }, /* @__PURE__ */ import_react18.default.createElement(import_fi16.FiX, { size: 12 })))), canEdit && /* @__PURE__ */ import_react18.default.createElement(
|
|
9061
9556
|
"input",
|
|
9062
9557
|
{
|
|
9063
9558
|
type: "text",
|
|
@@ -9200,9 +9695,12 @@ function RelationshipDetailsPanel({
|
|
|
9200
9695
|
const [description, setDescription] = (0, import_react20.useState)((link == null ? void 0 : link.description) ?? "");
|
|
9201
9696
|
const [customProps, setCustomProps] = (0, import_react20.useState)(() => extractCustomPropsFromNode(link || {}));
|
|
9202
9697
|
const [existingSections, setExistingSections] = (0, import_react20.useState)((link == null ? void 0 : link.description_sections) || []);
|
|
9698
|
+
const [sourceLabel, setSourceLabel] = (0, import_react20.useState)((link == null ? void 0 : link.source_label) ?? "");
|
|
9699
|
+
const [targetLabel, setTargetLabel] = (0, import_react20.useState)((link == null ? void 0 : link.target_label) ?? "");
|
|
9203
9700
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = (0, import_react20.useState)(false);
|
|
9204
9701
|
const [isSaving, setIsSaving] = (0, import_react20.useState)(false);
|
|
9205
9702
|
const [isReadMode, setIsReadMode] = (0, import_react20.useState)(false);
|
|
9703
|
+
const [hasUnsavedChanges, setHasUnsavedChanges] = (0, import_react20.useState)(false);
|
|
9206
9704
|
const propsEndRef = (0, import_react20.useRef)(null);
|
|
9207
9705
|
const canEdit = (0, import_react20.useMemo)(() => {
|
|
9208
9706
|
const ability = defineAbilityFor(userRole);
|
|
@@ -9213,12 +9711,16 @@ function RelationshipDetailsPanel({
|
|
|
9213
9711
|
setDescription((link == null ? void 0 : link.description) ?? "");
|
|
9214
9712
|
setExistingSections((link == null ? void 0 : link.description_sections) || []);
|
|
9215
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);
|
|
9216
9717
|
}, [link]);
|
|
9217
9718
|
const swallow = (e) => e.stopPropagation();
|
|
9218
9719
|
const handleAddProp = () => {
|
|
9219
9720
|
if (!canEdit) return;
|
|
9220
9721
|
const newProp = createNewCustomProperty(customProps);
|
|
9221
9722
|
setCustomProps((p) => [...p, newProp]);
|
|
9723
|
+
setHasUnsavedChanges(true);
|
|
9222
9724
|
setTimeout(() => {
|
|
9223
9725
|
var _a;
|
|
9224
9726
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -9230,6 +9732,12 @@ function RelationshipDetailsPanel({
|
|
|
9230
9732
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
9231
9733
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
9232
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
|
+
}
|
|
9233
9741
|
setIsSaving(true);
|
|
9234
9742
|
try {
|
|
9235
9743
|
const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
|
|
@@ -9245,8 +9753,11 @@ function RelationshipDetailsPanel({
|
|
|
9245
9753
|
isCurved: link.isCurved,
|
|
9246
9754
|
curveOffset: link.curveOffset
|
|
9247
9755
|
};
|
|
9756
|
+
if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
|
|
9757
|
+
if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
|
|
9248
9758
|
await onSave(dataToSave, keepOpen);
|
|
9249
9759
|
onDataUpdate(dataToSave);
|
|
9760
|
+
setHasUnsavedChanges(false);
|
|
9250
9761
|
if (!keepOpen) {
|
|
9251
9762
|
onClose();
|
|
9252
9763
|
}
|
|
@@ -9260,18 +9771,21 @@ function RelationshipDetailsPanel({
|
|
|
9260
9771
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
9261
9772
|
if (!canEdit) return;
|
|
9262
9773
|
setDescription(newDescription);
|
|
9774
|
+
setHasUnsavedChanges(true);
|
|
9263
9775
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9264
9776
|
triggerAutoSave({ description: newDescription });
|
|
9265
9777
|
};
|
|
9266
9778
|
const handleRemoveProp = (i) => {
|
|
9267
9779
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
9268
9780
|
setCustomProps(newProps);
|
|
9781
|
+
setHasUnsavedChanges(true);
|
|
9269
9782
|
triggerAutoSave({ customProps: newProps });
|
|
9270
9783
|
};
|
|
9271
9784
|
const handleUpdateProp = (index, updatedProp) => {
|
|
9272
9785
|
const newProps = [...customProps];
|
|
9273
9786
|
newProps[index] = updatedProp;
|
|
9274
9787
|
setCustomProps(newProps);
|
|
9788
|
+
setHasUnsavedChanges(true);
|
|
9275
9789
|
if (!updatedProp.isEditing) {
|
|
9276
9790
|
triggerAutoSave({ customProps: newProps });
|
|
9277
9791
|
}
|
|
@@ -9319,19 +9833,52 @@ function RelationshipDetailsPanel({
|
|
|
9319
9833
|
onImageClick: handleImageClickFromText,
|
|
9320
9834
|
onSaveDescription: handleSaveDescriptionInline
|
|
9321
9835
|
}
|
|
9322
|
-
) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div",
|
|
9836
|
+
) : /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-teal-400/0 via-teal-400/70 to-teal-400/0" }), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-teal-400/80 shadow-[0_0_18px_2px_rgba(45,212,191,0.55)]" }), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("h2", { className: "text-xl sm:text-2xl font-semibold tracking-tight" }, name || "Rela\xE7\xE3o")), /* @__PURE__ */ import_react20.default.createElement("button", { onClick: onClose, disabled: isSaving, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl disabled:opacity-50", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react20.default.createElement("div", { className: "px-6 pb-28 overflow-y-auto overscroll-contain space-y-4 max-h-[68vh] custom-scrollbar" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Nome da Rela\xE7\xE3o (Opcional)"), /* @__PURE__ */ import_react20.default.createElement(
|
|
9323
9837
|
"input",
|
|
9324
9838
|
{
|
|
9325
9839
|
type: "text",
|
|
9326
9840
|
value: name,
|
|
9327
|
-
onChange: (e) =>
|
|
9841
|
+
onChange: (e) => {
|
|
9842
|
+
setName(e.target.value);
|
|
9843
|
+
setHasUnsavedChanges(true);
|
|
9844
|
+
},
|
|
9328
9845
|
placeholder: "Ex: Controla, Pertence a, Fornece...",
|
|
9329
9846
|
disabled: !canEdit,
|
|
9330
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
|
|
9331
9848
|
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9332
9849
|
`
|
|
9333
9850
|
}
|
|
9334
|
-
)), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1.
|
|
9851
|
+
)), /* @__PURE__ */ import_react20.default.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react20.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ import_react20.default.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ import_react20.default.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ import_react20.default.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ import_react20.default.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ import_react20.default.createElement(
|
|
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__ */ import_react20.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ import_react20.default.createElement(
|
|
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__ */ import_react20.default.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ import_react20.default.createElement(
|
|
9335
9882
|
DescriptionDisplay,
|
|
9336
9883
|
{
|
|
9337
9884
|
description,
|
|
@@ -9401,6 +9948,7 @@ function RelationshipDetailsPanel({
|
|
|
9401
9948
|
onSave: (newDescription) => {
|
|
9402
9949
|
if (!canEdit) return;
|
|
9403
9950
|
setDescription(newDescription);
|
|
9951
|
+
setHasUnsavedChanges(true);
|
|
9404
9952
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9405
9953
|
triggerAutoSave({ description: newDescription });
|
|
9406
9954
|
},
|
|
@@ -9848,7 +10396,7 @@ function AncestryLinkDetailsPanel({ data, onClose, onOpenImageViewer, onOpenRefe
|
|
|
9848
10396
|
onMentionClick,
|
|
9849
10397
|
onImageClick: handleImageClickFromText
|
|
9850
10398
|
}
|
|
9851
|
-
) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div",
|
|
10399
|
+
) : /* @__PURE__ */ import_react24.default.createElement(import_react24.default.Fragment, null, /* @__PURE__ */ import_react24.default.createElement("div", { className: "h-[2px] bg-gradient-to-r from-blue-500/0 via-blue-500/70 to-blue-500/0" }), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pt-5 pb-3 flex items-start justify-between gap-4" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "min-w-0" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "inline-flex h-2.5 w-2.5 rounded-full bg-blue-500/80 shadow-[0_0_18px_2px_rgba(59,130,246,0.55)]" }), /* @__PURE__ */ import_react24.default.createElement("p", { className: "text-xs/relaxed text-slate-300" }, "Detalhes da Ancestralidade")), /* @__PURE__ */ import_react24.default.createElement("h2", { className: "text-lg font-semibold tracking-tight flex items-center gap-2" }, /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, sourceName), /* @__PURE__ */ import_react24.default.createElement("span", { className: "text-slate-500 text-sm" }, "\u2794"), /* @__PURE__ */ import_react24.default.createElement("span", { className: "truncate max-w-[150px]" }, targetName))), /* @__PURE__ */ import_react24.default.createElement("button", { onClick: onClose, className: "w-9 h-9 flex-shrink-0 grid place-content-center rounded-lg border border-white/15 bg-transparent hover:bg-white/5 transition-colors text-xl", title: "Fechar" }, "\xD7")), /* @__PURE__ */ import_react24.default.createElement("div", { className: "px-6 pb-6 overflow-y-auto overscroll-contain space-y-4 custom-scrollbar" }, description && /* @__PURE__ */ import_react24.default.createElement("div", { className: "space-y-1.5" }, /* @__PURE__ */ import_react24.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react24.default.createElement("label", { className: "text-xs text-slate-300 font-medium" }, "Descri\xE7\xE3o"), /* @__PURE__ */ import_react24.default.createElement(
|
|
9852
10400
|
"button",
|
|
9853
10401
|
{
|
|
9854
10402
|
onClick: () => setIsReadMode(true),
|
|
@@ -10487,7 +11035,8 @@ function XViewScene({
|
|
|
10487
11035
|
save_ancestry_board_action,
|
|
10488
11036
|
upload_file_action,
|
|
10489
11037
|
delete_file_action,
|
|
10490
|
-
check_user_permission
|
|
11038
|
+
check_user_permission,
|
|
11039
|
+
get_view_members
|
|
10491
11040
|
}) {
|
|
10492
11041
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
10493
11042
|
const { data: session, status } = (0, import_react27.useSession)();
|
|
@@ -10520,6 +11069,13 @@ function XViewScene({
|
|
|
10520
11069
|
} else {
|
|
10521
11070
|
setPermissionStatus("denied");
|
|
10522
11071
|
setIsLoading(false);
|
|
11072
|
+
return;
|
|
11073
|
+
}
|
|
11074
|
+
if (get_view_members) {
|
|
11075
|
+
const membersRes = await get_view_members(owner_id, type, id);
|
|
11076
|
+
if (membersRes.success && membersRes.members) {
|
|
11077
|
+
setViewMembers(membersRes.members);
|
|
11078
|
+
}
|
|
10523
11079
|
}
|
|
10524
11080
|
} catch (error) {
|
|
10525
11081
|
console.error("Erro ao verificar permiss\xE3o:", error);
|
|
@@ -10560,6 +11116,7 @@ function XViewScene({
|
|
|
10560
11116
|
const [isLoading, setIsLoading] = (0, import_react26.useState)(true);
|
|
10561
11117
|
const [permissionStatus, setPermissionStatus] = (0, import_react26.useState)("loading");
|
|
10562
11118
|
const [userPermissionRole, setUserPermissionRole] = (0, import_react26.useState)(null);
|
|
11119
|
+
const [viewMembers, setViewMembers] = (0, import_react26.useState)([]);
|
|
10563
11120
|
const [isInitialized, setIsInitialized] = (0, import_react26.useState)(false);
|
|
10564
11121
|
const [sceneVersion, setSceneVersion] = (0, import_react26.useState)(0);
|
|
10565
11122
|
const [contextMenu, setContextMenu] = (0, import_react26.useState)({
|
|
@@ -10628,6 +11185,7 @@ function XViewScene({
|
|
|
10628
11185
|
});
|
|
10629
11186
|
const [isImportModalOpen, setIsImportModalOpen] = (0, import_react26.useState)(false);
|
|
10630
11187
|
const [importSuccessMessage, setImportSuccessMessage] = (0, import_react26.useState)("");
|
|
11188
|
+
const [invalidTargetError, setInvalidTargetError] = (0, import_react26.useState)(null);
|
|
10631
11189
|
const [highlightedNodeId, setHighlightedNodeId] = (0, import_react26.useState)(null);
|
|
10632
11190
|
const [isAncestryBoardOpen, setIsAncestryBoardOpen] = (0, import_react26.useState)(false);
|
|
10633
11191
|
const [ancestryBoardData, setAncestryBoardData] = (0, import_react26.useState)([]);
|
|
@@ -12476,6 +13034,19 @@ function XViewScene({
|
|
|
12476
13034
|
const handleStartVersioning = (nodeData) => {
|
|
12477
13035
|
userActionHandlers.handleStartVersioning(actionHandlerContext, nodeData);
|
|
12478
13036
|
};
|
|
13037
|
+
const handleStartQuestQuick = (0, import_react26.useCallback)((questNode) => {
|
|
13038
|
+
var _a2;
|
|
13039
|
+
if (!questNode || !actionHandlerContext) return;
|
|
13040
|
+
const updatedNode = {
|
|
13041
|
+
...questNode,
|
|
13042
|
+
status: "In Progress",
|
|
13043
|
+
color: "#eab308",
|
|
13044
|
+
assignee_id: (_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id
|
|
13045
|
+
};
|
|
13046
|
+
if (userActionHandlers.handleSaveNodeDetails) {
|
|
13047
|
+
userActionHandlers.handleSaveNodeDetails(actionHandlerContext, updatedNode);
|
|
13048
|
+
}
|
|
13049
|
+
}, [session, actionHandlerContext]);
|
|
12479
13050
|
const handleCancelQuest = (0, import_react26.useCallback)(() => {
|
|
12480
13051
|
const { graphGroup, ghostElements } = stateRef.current;
|
|
12481
13052
|
if (ghostElements.node && graphGroup) {
|
|
@@ -14160,7 +14731,13 @@ function XViewScene({
|
|
|
14160
14731
|
if (!parentDataRef.current) {
|
|
14161
14732
|
return [];
|
|
14162
14733
|
}
|
|
14163
|
-
return Object.
|
|
14734
|
+
return Object.entries(parentDataRef.current).flatMap(([dbId, fileData]) => {
|
|
14735
|
+
const datasetName = fileData.dataset_name || `Dataset #${dbId.substring(0, 6)}`;
|
|
14736
|
+
return (fileData.nodes || []).map((node) => ({
|
|
14737
|
+
...node,
|
|
14738
|
+
dataset_name: datasetName
|
|
14739
|
+
}));
|
|
14740
|
+
}).filter((node) => {
|
|
14164
14741
|
var _a2;
|
|
14165
14742
|
return !((_a2 = node.version_node) == null ? void 0 : _a2.is_version);
|
|
14166
14743
|
});
|
|
@@ -14304,6 +14881,9 @@ function XViewScene({
|
|
|
14304
14881
|
}, 300);
|
|
14305
14882
|
} else {
|
|
14306
14883
|
setHasFocusedInitial(true);
|
|
14884
|
+
setInvalidTargetError(
|
|
14885
|
+
"O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
|
|
14886
|
+
);
|
|
14307
14887
|
}
|
|
14308
14888
|
}
|
|
14309
14889
|
}, [
|
|
@@ -14326,6 +14906,9 @@ function XViewScene({
|
|
|
14326
14906
|
}, 300);
|
|
14327
14907
|
} else {
|
|
14328
14908
|
setHasOpenedInitialAncestry(true);
|
|
14909
|
+
setInvalidTargetError(
|
|
14910
|
+
"O link aponta para uma ancestralidade que n\xE3o foi encontrada ou foi exclu\xEDda."
|
|
14911
|
+
);
|
|
14329
14912
|
}
|
|
14330
14913
|
}
|
|
14331
14914
|
}, [
|
|
@@ -14619,7 +15202,8 @@ function XViewScene({
|
|
|
14619
15202
|
availableNodes: allAvailableNodes,
|
|
14620
15203
|
availableAncestries: allAvailableAncestries,
|
|
14621
15204
|
viewName: viewParams == null ? void 0 : viewParams.name,
|
|
14622
|
-
questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1
|
|
15205
|
+
questCounter: ((_g = sceneDataRef.current) == null ? void 0 : _g.quest_counter) || 1,
|
|
15206
|
+
viewMembers
|
|
14623
15207
|
}
|
|
14624
15208
|
),
|
|
14625
15209
|
readingMode.isActive && readingMode.ancestry && /* @__PURE__ */ import_react26.default.createElement(
|
|
@@ -14749,7 +15333,8 @@ function XViewScene({
|
|
|
14749
15333
|
onMentionClick: handleAddExistingNode,
|
|
14750
15334
|
onUploadFile: upload_file_action,
|
|
14751
15335
|
userRole: userPermissionRole,
|
|
14752
|
-
currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName
|
|
15336
|
+
currentDatasetName: detailsNodeDatasetInfo == null ? void 0 : detailsNodeDatasetInfo.datasetName,
|
|
15337
|
+
viewMembers
|
|
14753
15338
|
}
|
|
14754
15339
|
),
|
|
14755
15340
|
detailsNode && !detailsNode.is_quest && /* @__PURE__ */ import_react26.default.createElement(
|
|
@@ -14862,7 +15447,10 @@ function XViewScene({
|
|
|
14862
15447
|
onRenderAncestry: handleStartReadingAncestry,
|
|
14863
15448
|
onEditAncestry: handleEditAncestry,
|
|
14864
15449
|
onDeleteAncestry: (ancestryId) => handleDeleteAncestry(ancestryId),
|
|
14865
|
-
onFocusNode: handleFocusNode
|
|
15450
|
+
onFocusNode: handleFocusNode,
|
|
15451
|
+
viewMembers,
|
|
15452
|
+
currentUser: session == null ? void 0 : session.user,
|
|
15453
|
+
onStartQuest: handleStartQuestQuick
|
|
14866
15454
|
}
|
|
14867
15455
|
),
|
|
14868
15456
|
/* @__PURE__ */ import_react26.default.createElement(
|
|
@@ -14935,6 +15523,83 @@ function XViewScene({
|
|
|
14935
15523
|
currentViewName: viewParams == null ? void 0 : viewParams.name,
|
|
14936
15524
|
currentAncestries: ancestryDataRef.current || []
|
|
14937
15525
|
}
|
|
15526
|
+
),
|
|
15527
|
+
invalidTargetError && /* @__PURE__ */ import_react26.default.createElement(
|
|
15528
|
+
"div",
|
|
15529
|
+
{
|
|
15530
|
+
className: "ui-overlay",
|
|
15531
|
+
style: {
|
|
15532
|
+
position: "fixed",
|
|
15533
|
+
top: "24px",
|
|
15534
|
+
left: "50%",
|
|
15535
|
+
transform: "translateX(-50%)",
|
|
15536
|
+
zIndex: 1e4,
|
|
15537
|
+
padding: "16px 24px",
|
|
15538
|
+
background: "rgba(30, 20, 20, 0.85)",
|
|
15539
|
+
backdropFilter: "blur(12px)",
|
|
15540
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
15541
|
+
border: "1px solid rgba(255, 70, 70, 0.35)",
|
|
15542
|
+
borderRadius: "16px",
|
|
15543
|
+
boxShadow: "0 12px 40px rgba(0,0,0,0.5), 0 0 30px rgba(255, 50, 50, 0.1)",
|
|
15544
|
+
color: "#ffa0a0",
|
|
15545
|
+
display: "flex",
|
|
15546
|
+
alignItems: "center",
|
|
15547
|
+
gap: "16px",
|
|
15548
|
+
fontFamily: "Inter, sans-serif",
|
|
15549
|
+
animation: "fadeInDown 0.5s cubic-bezier(0.16, 1, 0.3, 1)"
|
|
15550
|
+
}
|
|
15551
|
+
},
|
|
15552
|
+
/* @__PURE__ */ import_react26.default.createElement("style", null, `
|
|
15553
|
+
@keyframes fadeInDown {
|
|
15554
|
+
from { opacity: 0; transform: translate(-50%, -20px); }
|
|
15555
|
+
to { opacity: 1; transform: translate(-50%, 0); }
|
|
15556
|
+
}
|
|
15557
|
+
`),
|
|
15558
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
15559
|
+
"svg",
|
|
15560
|
+
{
|
|
15561
|
+
width: "20",
|
|
15562
|
+
height: "20",
|
|
15563
|
+
viewBox: "0 0 24 24",
|
|
15564
|
+
fill: "none",
|
|
15565
|
+
stroke: "currentColor",
|
|
15566
|
+
strokeWidth: "2",
|
|
15567
|
+
strokeLinecap: "round",
|
|
15568
|
+
strokeLinejoin: "round"
|
|
15569
|
+
},
|
|
15570
|
+
/* @__PURE__ */ import_react26.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
15571
|
+
/* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
15572
|
+
/* @__PURE__ */ import_react26.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
15573
|
+
),
|
|
15574
|
+
/* @__PURE__ */ import_react26.default.createElement("span", { style: { fontSize: "14px", fontWeight: 500 } }, invalidTargetError),
|
|
15575
|
+
/* @__PURE__ */ import_react26.default.createElement(
|
|
15576
|
+
"button",
|
|
15577
|
+
{
|
|
15578
|
+
onClick: () => setInvalidTargetError(null),
|
|
15579
|
+
style: {
|
|
15580
|
+
background: "rgba(255, 255, 255, 0.1)",
|
|
15581
|
+
border: "none",
|
|
15582
|
+
color: "white",
|
|
15583
|
+
padding: "4px 10px",
|
|
15584
|
+
borderRadius: "8px",
|
|
15585
|
+
cursor: "pointer",
|
|
15586
|
+
fontSize: "12px",
|
|
15587
|
+
fontWeight: 600,
|
|
15588
|
+
transition: "all 0.2s",
|
|
15589
|
+
marginLeft: "8px",
|
|
15590
|
+
border: "1px solid rgba(255,255,255,0.1)"
|
|
15591
|
+
},
|
|
15592
|
+
onMouseOver: (e) => {
|
|
15593
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.15)";
|
|
15594
|
+
e.currentTarget.style.transform = "translateY(-1px)";
|
|
15595
|
+
},
|
|
15596
|
+
onMouseOut: (e) => {
|
|
15597
|
+
e.currentTarget.style.background = "rgba(255, 255, 255, 0.1)";
|
|
15598
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
15599
|
+
}
|
|
15600
|
+
},
|
|
15601
|
+
"Fechar"
|
|
15602
|
+
)
|
|
14938
15603
|
)
|
|
14939
15604
|
);
|
|
14940
15605
|
}
|
|
@@ -15361,6 +16026,17 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
|
|
|
15361
16026
|
return { success: false, error: error.message };
|
|
15362
16027
|
}
|
|
15363
16028
|
}
|
|
16029
|
+
async function get_view_members_logic(db_services, ownerId, type, itemId) {
|
|
16030
|
+
try {
|
|
16031
|
+
if (!db_services.get_view_members) {
|
|
16032
|
+
return { success: false, error: "Servi\xE7o de busca de membros n\xE3o configurado." };
|
|
16033
|
+
}
|
|
16034
|
+
return await db_services.get_view_members(ownerId, type, itemId);
|
|
16035
|
+
} catch (error) {
|
|
16036
|
+
console.error("Erro em get_view_members_logic:", error);
|
|
16037
|
+
return { success: false, error: error.message };
|
|
16038
|
+
}
|
|
16039
|
+
}
|
|
15364
16040
|
// Annotate the CommonJS export names for ESM import in node:
|
|
15365
16041
|
0 && (module.exports = {
|
|
15366
16042
|
XViewScene,
|
|
@@ -15369,6 +16045,7 @@ async function delete_uploaded_file_logic(db_services, fileUrl) {
|
|
|
15369
16045
|
get_ancestry_file_logic,
|
|
15370
16046
|
get_scene_view_data_logic,
|
|
15371
16047
|
get_single_parent_file_logic,
|
|
16048
|
+
get_view_members_logic,
|
|
15372
16049
|
import_parent_file_modal_get_logic,
|
|
15373
16050
|
save_ancestry_board_logic,
|
|
15374
16051
|
save_view_data_logic,
|