@lv-x-software-house/x_view 1.2.5-dev.35 → 1.2.5-dev.36

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 CHANGED
@@ -622,8 +622,10 @@ function XViewSidebar({
622
622
  },
623
623
  isOpen,
624
624
  onToggle,
625
- userRole
626
- // 2. Receber a prop userRole
625
+ userRole,
626
+ viewMembers = [],
627
+ onActivateQuestLens = () => {
628
+ }
627
629
  }) {
628
630
  const [query, setQuery] = (0, import_react2.useState)("");
629
631
  const [showList, setShowList] = (0, import_react2.useState)(false);
@@ -633,12 +635,16 @@ function XViewSidebar({
633
635
  const [isFilterMenuOpen, setIsFilterMenuOpen] = (0, import_react2.useState)(false);
634
636
  const [filterCategory, setFilterCategory] = (0, import_react2.useState)("Type");
635
637
  const [filterValue, setFilterValue] = (0, import_react2.useState)("");
638
+ const [questFilterAssignee, setQuestFilterAssignee] = (0, import_react2.useState)("");
639
+ const [questFilterStatus, setQuestFilterStatus] = (0, import_react2.useState)("");
640
+ const [isQuestLensMenuOpen, setIsQuestLensMenuOpen] = (0, import_react2.useState)(false);
636
641
  const [isTypeDropdownOpen, setIsTypeDropdownOpen] = (0, import_react2.useState)(false);
637
642
  const [highlightedTypeIndex, setHighlightedTypeIndex] = (0, import_react2.useState)(-1);
638
643
  const containerRef = (0, import_react2.useRef)(null);
639
644
  const inputRef = (0, import_react2.useRef)(null);
640
645
  const filterMenuRef = (0, import_react2.useRef)(null);
641
646
  const typeDropdownRef = (0, import_react2.useRef)(null);
647
+ const questLensRef = (0, import_react2.useRef)(null);
642
648
  const ability = (0, import_react2.useMemo)(() => {
643
649
  return defineAbilityFor(userRole);
644
650
  }, [userRole]);
@@ -743,6 +749,17 @@ function XViewSidebar({
743
749
  }
744
750
  return () => document.removeEventListener("mousedown", handleClickOutside);
745
751
  }, [isFilterMenuOpen, isTypeDropdownOpen]);
752
+ (0, import_react2.useEffect)(() => {
753
+ const handleClickOutsideQuest = (event) => {
754
+ if (questLensRef.current && !questLensRef.current.contains(event.target)) {
755
+ setIsQuestLensMenuOpen(false);
756
+ }
757
+ };
758
+ if (isQuestLensMenuOpen) {
759
+ document.addEventListener("mousedown", handleClickOutsideQuest);
760
+ }
761
+ return () => document.removeEventListener("mousedown", handleClickOutsideQuest);
762
+ }, [isQuestLensMenuOpen]);
746
763
  (0, import_react2.useEffect)(() => {
747
764
  if (!query) setSelectedNodeId(null);
748
765
  }, [query]);
@@ -1034,7 +1051,58 @@ function XViewSidebar({
1034
1051
  },
1035
1052
  /* @__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: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ import_react2.default.createElement("polyline", { points: "2 12 12 17 22 12" })),
1036
1053
  "Ancestry Board"
1037
- ))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ import_react2.default.createElement(
1054
+ ), /* @__PURE__ */ import_react2.default.createElement("div", { className: "relative", ref: questLensRef }, /* @__PURE__ */ import_react2.default.createElement(
1055
+ "button",
1056
+ {
1057
+ onClick: () => setIsQuestLensMenuOpen(!isQuestLensMenuOpen),
1058
+ className: `${buttonBaseClass} ${isQuestLensMenuOpen || questFilterAssignee || questFilterStatus ? "bg-indigo-500/30 border-indigo-400" : ""}`,
1059
+ title: "Abrir filtros da Lente de Quests"
1060
+ },
1061
+ /* @__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("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }), /* @__PURE__ */ import_react2.default.createElement("path", { d: "M9 12l2 2 4-4" })),
1062
+ "Quest Lens",
1063
+ (questFilterAssignee || questFilterStatus) && /* @__PURE__ */ import_react2.default.createElement("span", { className: "ml-1 px-1.5 py-0.5 rounded-full bg-indigo-500 text-[9px] font-bold" }, "!")
1064
+ ), isQuestLensMenuOpen && /* @__PURE__ */ import_react2.default.createElement("div", { className: "absolute left-0 bottom-full mb-2 w-full p-4 rounded-xl bg-slate-900 border border-white/10 shadow-2xl z-50 flex flex-col gap-4 animate-in fade-in slide-in-from-bottom-2 duration-200" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Respons\xE1vel"), /* @__PURE__ */ import_react2.default.createElement(
1065
+ "select",
1066
+ {
1067
+ value: questFilterAssignee,
1068
+ onChange: (e) => setQuestFilterAssignee(e.target.value),
1069
+ 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 appearance-none cursor-pointer"
1070
+ },
1071
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "Todos os Respons\xE1veis"),
1072
+ viewMembers.map((m) => /* @__PURE__ */ import_react2.default.createElement("option", { key: m.id, value: m.id }, m.name || m.email))
1073
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Status"), /* @__PURE__ */ import_react2.default.createElement(
1074
+ "select",
1075
+ {
1076
+ value: questFilterStatus,
1077
+ onChange: (e) => setQuestFilterStatus(e.target.value),
1078
+ 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 appearance-none cursor-pointer"
1079
+ },
1080
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "Todos (Exclui Done)"),
1081
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Backlog" }, "Backlog"),
1082
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "In Progress" }, "In Progress"),
1083
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Review" }, "Review"),
1084
+ /* @__PURE__ */ import_react2.default.createElement("option", { value: "Done" }, "Done")
1085
+ )), /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex gap-2 pt-1" }, /* @__PURE__ */ import_react2.default.createElement(
1086
+ "button",
1087
+ {
1088
+ onClick: () => {
1089
+ setQuestFilterAssignee("");
1090
+ setQuestFilterStatus("");
1091
+ },
1092
+ className: "flex-1 py-2 bg-white/5 hover:bg-white/10 text-slate-300 text-[11px] font-medium rounded-lg transition-colors border border-white/5"
1093
+ },
1094
+ "Limpar"
1095
+ ), /* @__PURE__ */ import_react2.default.createElement(
1096
+ "button",
1097
+ {
1098
+ onClick: () => {
1099
+ onActivateQuestLens({ assigneeId: questFilterAssignee, status: questFilterStatus });
1100
+ setIsQuestLensMenuOpen(false);
1101
+ },
1102
+ className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95"
1103
+ },
1104
+ "Aplicar Lente"
1105
+ )))))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react2.default.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ import_react2.default.createElement(
1038
1106
  "button",
1039
1107
  {
1040
1108
  onClick: handleSaveClick,
@@ -11947,6 +12015,9 @@ var TIMELINE_NODE_SPACING_X = 20;
11947
12015
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
11948
12016
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
11949
12017
  var TIMELINE_GOLD_COLOR = 16766720;
12018
+ var QUEST_LENS_COLUMN_SPACING = 80;
12019
+ var QUEST_LENS_ROW_SPACING = 40;
12020
+ var QUEST_STATUS_ORDER = ["Backlog", "In Progress", "Review", "Done"];
11950
12021
  var getAllNodeIdsFromTree = (treeNode) => {
11951
12022
  const ids = /* @__PURE__ */ new Set();
11952
12023
  function traverse(node) {
@@ -12646,6 +12717,82 @@ function XViewScene({
12646
12717
  }
12647
12718
  });
12648
12719
  }, []);
12720
+ const handleActivateQuestLens = (0, import_react28.useCallback)(({ assigneeId, status: status2 }) => {
12721
+ var _a2;
12722
+ const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
12723
+ if (!nodeObjects || !tweenGroup || !graphGroup) return;
12724
+ const allQuests = ((_a2 = sceneDataRef.current) == null ? void 0 : _a2.quest_nodes) || [];
12725
+ const filteredQuests = allQuests.filter((q) => {
12726
+ const matchAssignee = !assigneeId || String(q.assignee_id) === String(assigneeId);
12727
+ const matchStatus = !status2 ? q.status !== "Done" : String(q.status) === String(status2);
12728
+ return matchAssignee && matchStatus;
12729
+ });
12730
+ if (filteredQuests.length === 0) {
12731
+ console.warn("Nenhuma quest encontrada para os filtros selecionados.");
12732
+ return;
12733
+ }
12734
+ const questsByStatus = {
12735
+ Backlog: [],
12736
+ "In Progress": [],
12737
+ Review: [],
12738
+ Done: []
12739
+ };
12740
+ filteredQuests.forEach((q) => {
12741
+ if (questsByStatus[q.status]) {
12742
+ questsByStatus[q.status].push(q);
12743
+ }
12744
+ });
12745
+ const startX = -((QUEST_STATUS_ORDER.length - 1) * QUEST_LENS_COLUMN_SPACING) / 2;
12746
+ Object.keys(nodeObjects).forEach((id) => {
12747
+ const mesh = nodeObjects[id];
12748
+ const isFilteredQuest = filteredQuests.some(
12749
+ (q) => String(q.id) === String(id)
12750
+ );
12751
+ if (!isFilteredQuest) {
12752
+ if (mesh.material) {
12753
+ const fadeTween = new import_tween2.Tween(mesh.material).to({ opacity: 0.15 }, 1200).easing(import_tween2.Easing.Cubic.Out);
12754
+ tweenGroup.add(fadeTween);
12755
+ fadeTween.start();
12756
+ }
12757
+ if (mesh.userData.labelObject) {
12758
+ mesh.userData.labelObject.visible = false;
12759
+ }
12760
+ } else {
12761
+ if (mesh.material) {
12762
+ const fadeInTween = new import_tween2.Tween(mesh.material).to({ opacity: 1 }, 1200).easing(import_tween2.Easing.Cubic.Out);
12763
+ tweenGroup.add(fadeInTween);
12764
+ fadeInTween.start();
12765
+ }
12766
+ if (mesh.userData.labelObject) {
12767
+ mesh.userData.labelObject.visible = true;
12768
+ }
12769
+ }
12770
+ });
12771
+ QUEST_STATUS_ORDER.forEach((statusName, colIndex) => {
12772
+ if (status2 && status2 !== statusName) return;
12773
+ const questsInCol = questsByStatus[statusName];
12774
+ const colX = startX + colIndex * QUEST_LENS_COLUMN_SPACING;
12775
+ questsInCol.forEach((q, rowIndex) => {
12776
+ const nodeId = String(q.id);
12777
+ let mesh = nodeObjects[nodeId];
12778
+ if (!mesh) {
12779
+ mesh = addOrUpdateNodeMesh(q, new THREE3.Vector3(colX, 150, 0), true);
12780
+ }
12781
+ const targetY = (questsInCol.length - 1) * 0.5 * QUEST_LENS_ROW_SPACING - rowIndex * QUEST_LENS_ROW_SPACING;
12782
+ const lensTween = new import_tween2.Tween(mesh.position).to({ x: colX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
12783
+ tweenGroup.add(lensTween);
12784
+ lensTween.start();
12785
+ if (mesh.userData.labelObject) {
12786
+ const labelTargetPos = new THREE3.Vector3(colX, targetY, 0).add(
12787
+ mesh.userData.labelOffset || new THREE3.Vector3(0, -2.5, 0)
12788
+ );
12789
+ const labelTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTargetPos, 1500).easing(import_tween2.Easing.Cubic.InOut);
12790
+ tweenGroup.add(labelTween);
12791
+ labelTween.start();
12792
+ }
12793
+ });
12794
+ });
12795
+ }, [addOrUpdateNodeMesh]);
12649
12796
  const handleVersionTimeline = (0, import_react28.useCallback)((sourceMesh, versionMeshes) => {
12650
12797
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
12651
12798
  if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
@@ -16166,13 +16313,15 @@ function XViewScene({
16166
16313
  onOpenImportModal: () => setIsImportModalOpen(true),
16167
16314
  importSuccessMessage,
16168
16315
  onActivateTimeline: handleActivateTimeline,
16316
+ onActivateQuestLens: handleActivateQuestLens,
16169
16317
  onOpenAncestryBoard: handleOpenAncestryBoard,
16170
16318
  viewType: viewParams == null ? void 0 : viewParams.type,
16171
16319
  viewName: viewParams == null ? void 0 : viewParams.name,
16172
16320
  onNavigateBack: handleNavigateBack,
16173
16321
  isOpen: isSidebarOpen,
16174
16322
  onToggle: setIsSidebarOpen,
16175
- userRole: userPermissionRole
16323
+ userRole: userPermissionRole,
16324
+ viewMembers
16176
16325
  }
16177
16326
  ),
16178
16327
  creationMode.isActive && /* @__PURE__ */ import_react28.default.createElement(
package/dist/index.mjs CHANGED
@@ -583,8 +583,10 @@ function XViewSidebar({
583
583
  },
584
584
  isOpen,
585
585
  onToggle,
586
- userRole
587
- // 2. Receber a prop userRole
586
+ userRole,
587
+ viewMembers = [],
588
+ onActivateQuestLens = () => {
589
+ }
588
590
  }) {
589
591
  const [query, setQuery] = useState2("");
590
592
  const [showList, setShowList] = useState2(false);
@@ -594,12 +596,16 @@ function XViewSidebar({
594
596
  const [isFilterMenuOpen, setIsFilterMenuOpen] = useState2(false);
595
597
  const [filterCategory, setFilterCategory] = useState2("Type");
596
598
  const [filterValue, setFilterValue] = useState2("");
599
+ const [questFilterAssignee, setQuestFilterAssignee] = useState2("");
600
+ const [questFilterStatus, setQuestFilterStatus] = useState2("");
601
+ const [isQuestLensMenuOpen, setIsQuestLensMenuOpen] = useState2(false);
597
602
  const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState2(false);
598
603
  const [highlightedTypeIndex, setHighlightedTypeIndex] = useState2(-1);
599
604
  const containerRef = useRef2(null);
600
605
  const inputRef = useRef2(null);
601
606
  const filterMenuRef = useRef2(null);
602
607
  const typeDropdownRef = useRef2(null);
608
+ const questLensRef = useRef2(null);
603
609
  const ability = useMemo2(() => {
604
610
  return defineAbilityFor(userRole);
605
611
  }, [userRole]);
@@ -704,6 +710,17 @@ function XViewSidebar({
704
710
  }
705
711
  return () => document.removeEventListener("mousedown", handleClickOutside);
706
712
  }, [isFilterMenuOpen, isTypeDropdownOpen]);
713
+ useEffect2(() => {
714
+ const handleClickOutsideQuest = (event) => {
715
+ if (questLensRef.current && !questLensRef.current.contains(event.target)) {
716
+ setIsQuestLensMenuOpen(false);
717
+ }
718
+ };
719
+ if (isQuestLensMenuOpen) {
720
+ document.addEventListener("mousedown", handleClickOutsideQuest);
721
+ }
722
+ return () => document.removeEventListener("mousedown", handleClickOutsideQuest);
723
+ }, [isQuestLensMenuOpen]);
707
724
  useEffect2(() => {
708
725
  if (!query) setSelectedNodeId(null);
709
726
  }, [query]);
@@ -995,7 +1012,58 @@ function XViewSidebar({
995
1012
  },
996
1013
  /* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ React2.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ React2.createElement("polyline", { points: "2 12 12 17 22 12" })),
997
1014
  "Ancestry Board"
998
- ))), /* @__PURE__ */ React2.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ React2.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ React2.createElement(
1015
+ ), /* @__PURE__ */ React2.createElement("div", { className: "relative", ref: questLensRef }, /* @__PURE__ */ React2.createElement(
1016
+ "button",
1017
+ {
1018
+ onClick: () => setIsQuestLensMenuOpen(!isQuestLensMenuOpen),
1019
+ className: `${buttonBaseClass} ${isQuestLensMenuOpen || questFilterAssignee || questFilterStatus ? "bg-indigo-500/30 border-indigo-400" : ""}`,
1020
+ title: "Abrir filtros da Lente de Quests"
1021
+ },
1022
+ /* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }), /* @__PURE__ */ React2.createElement("path", { d: "M9 12l2 2 4-4" })),
1023
+ "Quest Lens",
1024
+ (questFilterAssignee || questFilterStatus) && /* @__PURE__ */ React2.createElement("span", { className: "ml-1 px-1.5 py-0.5 rounded-full bg-indigo-500 text-[9px] font-bold" }, "!")
1025
+ ), isQuestLensMenuOpen && /* @__PURE__ */ React2.createElement("div", { className: "absolute left-0 bottom-full mb-2 w-full p-4 rounded-xl bg-slate-900 border border-white/10 shadow-2xl z-50 flex flex-col gap-4 animate-in fade-in slide-in-from-bottom-2 duration-200" }, /* @__PURE__ */ React2.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Respons\xE1vel"), /* @__PURE__ */ React2.createElement(
1026
+ "select",
1027
+ {
1028
+ value: questFilterAssignee,
1029
+ onChange: (e) => setQuestFilterAssignee(e.target.value),
1030
+ 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 appearance-none cursor-pointer"
1031
+ },
1032
+ /* @__PURE__ */ React2.createElement("option", { value: "" }, "Todos os Respons\xE1veis"),
1033
+ viewMembers.map((m) => /* @__PURE__ */ React2.createElement("option", { key: m.id, value: m.id }, m.name || m.email))
1034
+ )), /* @__PURE__ */ React2.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Status"), /* @__PURE__ */ React2.createElement(
1035
+ "select",
1036
+ {
1037
+ value: questFilterStatus,
1038
+ onChange: (e) => setQuestFilterStatus(e.target.value),
1039
+ 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 appearance-none cursor-pointer"
1040
+ },
1041
+ /* @__PURE__ */ React2.createElement("option", { value: "" }, "Todos (Exclui Done)"),
1042
+ /* @__PURE__ */ React2.createElement("option", { value: "Backlog" }, "Backlog"),
1043
+ /* @__PURE__ */ React2.createElement("option", { value: "In Progress" }, "In Progress"),
1044
+ /* @__PURE__ */ React2.createElement("option", { value: "Review" }, "Review"),
1045
+ /* @__PURE__ */ React2.createElement("option", { value: "Done" }, "Done")
1046
+ )), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2 pt-1" }, /* @__PURE__ */ React2.createElement(
1047
+ "button",
1048
+ {
1049
+ onClick: () => {
1050
+ setQuestFilterAssignee("");
1051
+ setQuestFilterStatus("");
1052
+ },
1053
+ className: "flex-1 py-2 bg-white/5 hover:bg-white/10 text-slate-300 text-[11px] font-medium rounded-lg transition-colors border border-white/5"
1054
+ },
1055
+ "Limpar"
1056
+ ), /* @__PURE__ */ React2.createElement(
1057
+ "button",
1058
+ {
1059
+ onClick: () => {
1060
+ onActivateQuestLens({ assigneeId: questFilterAssignee, status: questFilterStatus });
1061
+ setIsQuestLensMenuOpen(false);
1062
+ },
1063
+ className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95"
1064
+ },
1065
+ "Aplicar Lente"
1066
+ )))))), /* @__PURE__ */ React2.createElement("div", { className: "px-4 py-3 border-t border-white/10 bg-slate-900/30" }, /* @__PURE__ */ React2.createElement("section", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Navega\xE7\xE3o")), ability.can("update", "Scene") && /* @__PURE__ */ React2.createElement(
999
1067
  "button",
1000
1068
  {
1001
1069
  onClick: handleSaveClick,
@@ -11989,6 +12057,9 @@ var TIMELINE_NODE_SPACING_X = 20;
11989
12057
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
11990
12058
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
11991
12059
  var TIMELINE_GOLD_COLOR = 16766720;
12060
+ var QUEST_LENS_COLUMN_SPACING = 80;
12061
+ var QUEST_LENS_ROW_SPACING = 40;
12062
+ var QUEST_STATUS_ORDER = ["Backlog", "In Progress", "Review", "Done"];
11992
12063
  var getAllNodeIdsFromTree = (treeNode) => {
11993
12064
  const ids = /* @__PURE__ */ new Set();
11994
12065
  function traverse(node) {
@@ -12688,6 +12759,82 @@ function XViewScene({
12688
12759
  }
12689
12760
  });
12690
12761
  }, []);
12762
+ const handleActivateQuestLens = useCallback4(({ assigneeId, status: status2 }) => {
12763
+ var _a2;
12764
+ const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
12765
+ if (!nodeObjects || !tweenGroup || !graphGroup) return;
12766
+ const allQuests = ((_a2 = sceneDataRef.current) == null ? void 0 : _a2.quest_nodes) || [];
12767
+ const filteredQuests = allQuests.filter((q) => {
12768
+ const matchAssignee = !assigneeId || String(q.assignee_id) === String(assigneeId);
12769
+ const matchStatus = !status2 ? q.status !== "Done" : String(q.status) === String(status2);
12770
+ return matchAssignee && matchStatus;
12771
+ });
12772
+ if (filteredQuests.length === 0) {
12773
+ console.warn("Nenhuma quest encontrada para os filtros selecionados.");
12774
+ return;
12775
+ }
12776
+ const questsByStatus = {
12777
+ Backlog: [],
12778
+ "In Progress": [],
12779
+ Review: [],
12780
+ Done: []
12781
+ };
12782
+ filteredQuests.forEach((q) => {
12783
+ if (questsByStatus[q.status]) {
12784
+ questsByStatus[q.status].push(q);
12785
+ }
12786
+ });
12787
+ const startX = -((QUEST_STATUS_ORDER.length - 1) * QUEST_LENS_COLUMN_SPACING) / 2;
12788
+ Object.keys(nodeObjects).forEach((id) => {
12789
+ const mesh = nodeObjects[id];
12790
+ const isFilteredQuest = filteredQuests.some(
12791
+ (q) => String(q.id) === String(id)
12792
+ );
12793
+ if (!isFilteredQuest) {
12794
+ if (mesh.material) {
12795
+ const fadeTween = new Tween2(mesh.material).to({ opacity: 0.15 }, 1200).easing(Easing2.Cubic.Out);
12796
+ tweenGroup.add(fadeTween);
12797
+ fadeTween.start();
12798
+ }
12799
+ if (mesh.userData.labelObject) {
12800
+ mesh.userData.labelObject.visible = false;
12801
+ }
12802
+ } else {
12803
+ if (mesh.material) {
12804
+ const fadeInTween = new Tween2(mesh.material).to({ opacity: 1 }, 1200).easing(Easing2.Cubic.Out);
12805
+ tweenGroup.add(fadeInTween);
12806
+ fadeInTween.start();
12807
+ }
12808
+ if (mesh.userData.labelObject) {
12809
+ mesh.userData.labelObject.visible = true;
12810
+ }
12811
+ }
12812
+ });
12813
+ QUEST_STATUS_ORDER.forEach((statusName, colIndex) => {
12814
+ if (status2 && status2 !== statusName) return;
12815
+ const questsInCol = questsByStatus[statusName];
12816
+ const colX = startX + colIndex * QUEST_LENS_COLUMN_SPACING;
12817
+ questsInCol.forEach((q, rowIndex) => {
12818
+ const nodeId = String(q.id);
12819
+ let mesh = nodeObjects[nodeId];
12820
+ if (!mesh) {
12821
+ mesh = addOrUpdateNodeMesh(q, new THREE3.Vector3(colX, 150, 0), true);
12822
+ }
12823
+ const targetY = (questsInCol.length - 1) * 0.5 * QUEST_LENS_ROW_SPACING - rowIndex * QUEST_LENS_ROW_SPACING;
12824
+ const lensTween = new Tween2(mesh.position).to({ x: colX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
12825
+ tweenGroup.add(lensTween);
12826
+ lensTween.start();
12827
+ if (mesh.userData.labelObject) {
12828
+ const labelTargetPos = new THREE3.Vector3(colX, targetY, 0).add(
12829
+ mesh.userData.labelOffset || new THREE3.Vector3(0, -2.5, 0)
12830
+ );
12831
+ const labelTween = new Tween2(mesh.userData.labelObject.position).to(labelTargetPos, 1500).easing(Easing2.Cubic.InOut);
12832
+ tweenGroup.add(labelTween);
12833
+ labelTween.start();
12834
+ }
12835
+ });
12836
+ });
12837
+ }, [addOrUpdateNodeMesh]);
12691
12838
  const handleVersionTimeline = useCallback4((sourceMesh, versionMeshes) => {
12692
12839
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
12693
12840
  if (!tweenGroup || !timelineIntervalsGroup || versionMeshes.length === 0)
@@ -16208,13 +16355,15 @@ function XViewScene({
16208
16355
  onOpenImportModal: () => setIsImportModalOpen(true),
16209
16356
  importSuccessMessage,
16210
16357
  onActivateTimeline: handleActivateTimeline,
16358
+ onActivateQuestLens: handleActivateQuestLens,
16211
16359
  onOpenAncestryBoard: handleOpenAncestryBoard,
16212
16360
  viewType: viewParams == null ? void 0 : viewParams.type,
16213
16361
  viewName: viewParams == null ? void 0 : viewParams.name,
16214
16362
  onNavigateBack: handleNavigateBack,
16215
16363
  isOpen: isSidebarOpen,
16216
16364
  onToggle: setIsSidebarOpen,
16217
- userRole: userPermissionRole
16365
+ userRole: userPermissionRole,
16366
+ viewMembers
16218
16367
  }
16219
16368
  ),
16220
16369
  creationMode.isActive && /* @__PURE__ */ React27.createElement(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.5-dev.35",
3
+ "version": "1.2.5-dev.36",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",