@lv-x-software-house/x_view 1.2.5-dev.32 → 1.2.5-dev.33

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
@@ -9724,12 +9724,16 @@ var EVENT_ICONS = {
9724
9724
  function QuestTimeline({
9725
9725
  timeline = [],
9726
9726
  viewMembers = [],
9727
- onOpenImageViewer
9727
+ onOpenImageViewer,
9728
+ onAddNote,
9729
+ // Nova prop para adicionar nota a uma etapa específica
9730
+ canEdit = true
9728
9731
  }) {
9729
9732
  const sortedTimeline = [...timeline].sort((a, b) => b.timestamp - a.timestamp);
9730
9733
  const getMemberName = (userId) => {
9731
- const member = viewMembers.find((m) => m.id === userId);
9732
- return (member == null ? void 0 : member.name) || (member == null ? void 0 : member.email) || "Sistema";
9734
+ if (!userId) return "Sistema";
9735
+ const member = viewMembers.find((m) => String(m.id) === String(userId));
9736
+ return (member == null ? void 0 : member.name) || (member == null ? void 0 : member.email) || userId;
9733
9737
  };
9734
9738
  const formatDate = (timestamp) => {
9735
9739
  const date = new Date(timestamp);
@@ -9747,7 +9751,19 @@ function QuestTimeline({
9747
9751
  const config = EVENT_ICONS[event.type] || EVENT_ICONS.default;
9748
9752
  const Icon = config.icon;
9749
9753
  const actorName = getMemberName(event.actorId);
9750
- return /* @__PURE__ */ import_react19.default.createElement("div", { key: index, className: "relative animate-in fade-in slide-in-from-left-2 duration-300", style: { animationDelay: `${index * 50}ms` } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: `absolute -left-[35px] top-0 w-8 h-8 rounded-full ${config.bg} flex items-center justify-center ring-4 ring-slate-950 border border-white/5` }, /* @__PURE__ */ import_react19.default.createElement(Icon, { className: config.iconColor || config.color, size: 14 })), /* @__PURE__ */ import_react19.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-xs font-semibold text-slate-200" }, actorName), /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-[10px] text-slate-500 tabular-nums" }, formatDate(event.timestamp))), /* @__PURE__ */ import_react19.default.createElement("p", { className: "text-sm text-slate-400 leading-relaxed" }, event.description), (event.from_status || event.to_status) && /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex items-center gap-1.5 py-1 px-2 rounded-md bg-white/5 border border-white/5 w-fit mt-2" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-[10px] text-slate-500" }, event.from_status || "?"), /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiArrowRight, { size: 10, className: "text-slate-600" }), /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-[10px] text-indigo-300 font-bold" }, event.to_status || "?")), event.metadata && Object.keys(event.metadata).length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "mt-2 text-[10px] grid grid-cols-2 gap-2 p-2 bg-slate-900/50 rounded border border-white/5" }, Object.entries(event.metadata).map(([key, value]) => /* @__PURE__ */ import_react19.default.createElement("div", { key, className: "flex flex-col gap-0.5" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-slate-500 capitalize" }, key.replace(/_/g, " ")), /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-slate-300 truncate font-mono" }, typeof value === "string" ? value : JSON.stringify(value))))), event.links && event.links.length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, event.links.map((link, lIdx) => /* @__PURE__ */ import_react19.default.createElement(
9754
+ return /* @__PURE__ */ import_react19.default.createElement("div", { key: event.id || index, className: "relative animate-in fade-in slide-in-from-left-2 duration-300", style: { animationDelay: `${index * 50}ms` } }, /* @__PURE__ */ import_react19.default.createElement("div", { className: `absolute -left-[35px] top-0 w-8 h-8 rounded-full ${config.bg} flex items-center justify-center ring-4 ring-slate-950 border border-white/5` }, /* @__PURE__ */ import_react19.default.createElement(Icon, { className: config.iconColor || config.color, size: 14 })), /* @__PURE__ */ import_react19.default.createElement("div", { className: "group space-y-1" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-xs font-semibold text-slate-200" }, actorName), canEdit && onAddNote && /* @__PURE__ */ import_react19.default.createElement(
9755
+ "button",
9756
+ {
9757
+ onClick: () => onAddNote(event),
9758
+ className: "opacity-0 group-hover:opacity-100 p-1 rounded-md hover:bg-white/5 text-slate-500 hover:text-indigo-400 transition-all",
9759
+ title: "Adicionar detalhe/anexo a esta etapa"
9760
+ },
9761
+ /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiPlusCircle, { size: 12 })
9762
+ )), /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-[10px] text-slate-500 tabular-nums" }, formatDate(event.timestamp))), /* @__PURE__ */ import_react19.default.createElement("p", { className: "text-sm text-slate-400 leading-relaxed" }, event.description), event.notes && /* @__PURE__ */ import_react19.default.createElement("div", { className: "mt-1 py-1 px-2 rounded bg-indigo-500/5 border-l-2 border-indigo-500/30" }, /* @__PURE__ */ import_react19.default.createElement("p", { className: "text-[11px] text-slate-300 italic whitespace-pre-wrap" }, event.notes)), (event.from_status || event.to_status) && /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex items-center gap-1.5 py-1 px-2 rounded-md bg-white/5 border border-white/5 w-fit mt-2" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-[10px] text-slate-500" }, event.from_status || "?"), /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiArrowRight, { size: 10, className: "text-slate-600" }), /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-[10px] text-indigo-300 font-bold" }, event.to_status || "?")), event.metadata && Object.keys(event.metadata).length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "mt-2 text-[10px] grid grid-cols-2 gap-2 p-2 bg-slate-900/50 rounded border border-white/5" }, Object.entries(event.metadata).map(([key, value]) => {
9763
+ const isAssigneeId = key.toLowerCase().includes("assignee_id") || key.toLowerCase().includes("respons\xE1vel") || key.toLowerCase().includes("assignee id");
9764
+ const displayValue = isAssigneeId && typeof value === "string" ? getMemberName(value) : typeof value === "string" ? value : JSON.stringify(value);
9765
+ return /* @__PURE__ */ import_react19.default.createElement("div", { key, className: "flex flex-col gap-0.5" }, /* @__PURE__ */ import_react19.default.createElement("span", { className: "text-slate-500 capitalize" }, key.replace(/_/g, " ")), /* @__PURE__ */ import_react19.default.createElement("span", { className: `${isAssigneeId ? "text-indigo-300 font-medium" : "text-slate-300"} truncate font-mono` }, displayValue));
9766
+ })), event.links && event.links.length > 0 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, event.links.map((link, lIdx) => /* @__PURE__ */ import_react19.default.createElement(
9751
9767
  "a",
9752
9768
  {
9753
9769
  key: lIdx,
@@ -9764,10 +9780,10 @@ function QuestTimeline({
9764
9780
  key: iIdx,
9765
9781
  type: "button",
9766
9782
  onClick: () => onOpenImageViewer == null ? void 0 : onOpenImageViewer(event.images, iIdx),
9767
- className: "relative w-12 h-12 rounded border border-white/10 overflow-hidden hover:border-sky-500/50 transition-colors group"
9783
+ className: "relative w-12 h-12 rounded border border-white/10 overflow-hidden hover:border-sky-500/50 transition-colors group/img"
9768
9784
  },
9769
9785
  /* @__PURE__ */ import_react19.default.createElement("img", { src: img.value, alt: img.name, className: "w-full h-full object-cover" }),
9770
- /* @__PURE__ */ import_react19.default.createElement("div", { className: "absolute inset-0 bg-slate-900/40 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-opacity" }, /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiSearch, { size: 12, className: "text-white" }))
9786
+ /* @__PURE__ */ import_react19.default.createElement("div", { className: "absolute inset-0 bg-slate-900/40 opacity-0 group-hover/img:opacity-100 flex items-center justify-center transition-opacity" }, /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiSearch, { size: 12, className: "text-white" }))
9771
9787
  )))));
9772
9788
  })));
9773
9789
  }
@@ -9833,6 +9849,8 @@ function QuestDetailsPanel({
9833
9849
  const [noteLinks, setNoteLinks] = (0, import_react20.useState)([]);
9834
9850
  const [noteImages, setNoteImages] = (0, import_react20.useState)([]);
9835
9851
  const [isNoteFormOpen, setIsNoteFormOpen] = (0, import_react20.useState)(false);
9852
+ const [editingEntryId, setEditingEntryId] = (0, import_react20.useState)(null);
9853
+ const [editingEntryLabel, setEditingEntryLabel] = (0, import_react20.useState)("");
9836
9854
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
9837
9855
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
9838
9856
  initialWidth: isReadMode ? 700 : 440,
@@ -9964,14 +9982,30 @@ function QuestDetailsPanel({
9964
9982
  }
9965
9983
  };
9966
9984
  const handleAddTimelineNote = async () => {
9967
- if (!noteText.trim() || isSaving) return;
9985
+ if (!noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving) return;
9968
9986
  setIsSaving(true);
9969
9987
  const actorId = (currentUser == null ? void 0 : currentUser.id) || "unknown";
9970
- const newEntry = createTimelineEntry(actorId, noteText, "comment", {
9971
- links: noteLinks,
9972
- images: noteImages
9973
- });
9974
- const updatedTimeline = [...timeline, newEntry];
9988
+ let updatedTimeline;
9989
+ if (editingEntryId) {
9990
+ updatedTimeline = timeline.map((entry) => {
9991
+ if (entry.id === editingEntryId) {
9992
+ return {
9993
+ ...entry,
9994
+ notes: noteText.trim() ? entry.notes ? `${entry.notes}
9995
+ ${noteText}` : noteText : entry.notes,
9996
+ links: [...entry.links || [], ...noteLinks],
9997
+ images: [...entry.images || [], ...noteImages]
9998
+ };
9999
+ }
10000
+ return entry;
10001
+ });
10002
+ } else {
10003
+ const newEntry = createTimelineEntry(actorId, noteText, "comment", {
10004
+ links: noteLinks,
10005
+ images: noteImages
10006
+ });
10007
+ updatedTimeline = [...timeline, newEntry];
10008
+ }
9975
10009
  try {
9976
10010
  const nodeToSave = {
9977
10011
  ...node,
@@ -9984,12 +10018,26 @@ function QuestDetailsPanel({
9984
10018
  setNoteLinks([]);
9985
10019
  setNoteImages([]);
9986
10020
  setIsNoteFormOpen(false);
10021
+ setEditingEntryId(null);
10022
+ setEditingEntryLabel("");
9987
10023
  } catch (error) {
9988
10024
  console.error("Erro ao adicionar nota:", error);
9989
10025
  } finally {
9990
10026
  setIsSaving(false);
9991
10027
  }
9992
10028
  };
10029
+ const handleOpenNoteForEntry = (entry) => {
10030
+ setEditingEntryId(entry.id);
10031
+ setEditingEntryLabel(entry.description);
10032
+ setNoteText("");
10033
+ setNoteLinks([]);
10034
+ setNoteImages([]);
10035
+ setIsNoteFormOpen(true);
10036
+ setTimeout(() => {
10037
+ const form = document.getElementById("quest-note-form");
10038
+ form == null ? void 0 : form.scrollIntoView({ behavior: "smooth", block: "center" });
10039
+ }, 100);
10040
+ };
9993
10041
  const addLinkToNote = () => {
9994
10042
  setNoteLinks([...noteLinks, { id: v4_default(), name: "", value: "" }]);
9995
10043
  };
@@ -10351,17 +10399,32 @@ function QuestDetailsPanel({
10351
10399
  "button",
10352
10400
  {
10353
10401
  type: "button",
10354
- onClick: () => setIsNoteFormOpen(!isNoteFormOpen),
10402
+ onClick: () => {
10403
+ if (isNoteFormOpen && editingEntryId) {
10404
+ setEditingEntryId(null);
10405
+ setEditingEntryLabel("");
10406
+ setNoteText("");
10407
+ setNoteLinks([]);
10408
+ setNoteImages([]);
10409
+ } else {
10410
+ setIsNoteFormOpen(!isNoteFormOpen);
10411
+ setEditingEntryId(null);
10412
+ setEditingEntryLabel("");
10413
+ }
10414
+ },
10355
10415
  className: "flex items-center gap-1.5 px-2 py-1 text-[10px] rounded-md bg-white/5 hover:bg-white/10 border border-white/10 transition-colors text-slate-300"
10356
10416
  },
10357
- isNoteFormOpen ? /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiX, { size: 12 }) : /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiPlus, { size: 12 }),
10358
- isNoteFormOpen ? "Cancelar" : "Nova Nota"
10359
- )), isNoteFormOpen && /* @__PURE__ */ import_react20.default.createElement("div", { className: "mb-6 p-4 rounded-xl bg-white/5 border border-white/5 space-y-3 animate-in fade-in slide-in-from-top-2" }, /* @__PURE__ */ import_react20.default.createElement(
10417
+ isNoteFormOpen && !editingEntryId ? /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiX, { size: 12 }) : /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiPlus, { size: 12 }),
10418
+ isNoteFormOpen && !editingEntryId ? "Cancelar" : "Nova Nota"
10419
+ )), isNoteFormOpen && /* @__PURE__ */ import_react20.default.createElement("div", { id: "quest-note-form", className: "mb-6 p-4 rounded-xl bg-white/5 border border-white/5 space-y-3 animate-in fade-in slide-in-from-top-2 border-l-4 border-l-indigo-500/40" }, editingEntryId && /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center justify-between mb-2 pb-2 border-b border-white/5" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-[10px] text-indigo-400 font-bold uppercase tracking-wider" }, "Adicionando \xE0 etapa:"), /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-xs text-slate-300 italic truncate max-w-[250px]" }, editingEntryLabel)), /* @__PURE__ */ import_react20.default.createElement("button", { type: "button", onClick: () => {
10420
+ setIsNoteFormOpen(false);
10421
+ setEditingEntryId(null);
10422
+ }, className: "text-slate-500 hover:text-white" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiX, { size: 14 }))), /* @__PURE__ */ import_react20.default.createElement(
10360
10423
  "textarea",
10361
10424
  {
10362
10425
  value: noteText,
10363
10426
  onChange: (e) => setNoteText(e.target.value),
10364
- placeholder: "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10427
+ placeholder: editingEntryId ? "Adicione uma observa\xE7\xE3o a esta etapa..." : "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10365
10428
  className: "w-full bg-slate-900/50 p-3 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-1 focus:ring-indigo-500/50 min-h-[80px] resize-none"
10366
10429
  }
10367
10430
  ), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ import_react20.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase font-bold tracking-tight" }, "Links"), /* @__PURE__ */ import_react20.default.createElement("button", { type: "button", onClick: addLinkToNote, className: "text-indigo-400 hover:text-indigo-300 text-[10px] flex items-center gap-1 font-medium" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiPlus, { size: 10 }), " Adicionar Link")), noteLinks.length > 0 && /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-2" }, noteLinks.map((link) => /* @__PURE__ */ import_react20.default.createElement("div", { key: link.id, className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-1" }, /* @__PURE__ */ import_react20.default.createElement(
@@ -10396,16 +10459,18 @@ function QuestDetailsPanel({
10396
10459
  {
10397
10460
  type: "button",
10398
10461
  onClick: handleAddTimelineNote,
10399
- disabled: !noteText.trim() || isSaving,
10462
+ disabled: !noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving,
10400
10463
  className: "px-3 py-1.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-semibold transition-colors disabled:opacity-50"
10401
10464
  },
10402
- "Adicionar ao Hist\xF3rico"
10465
+ editingEntryId ? "Salvar nesta Etapa" : "Adicionar ao Hist\xF3rico"
10403
10466
  ))), /* @__PURE__ */ import_react20.default.createElement(
10404
10467
  QuestTimeline,
10405
10468
  {
10406
10469
  timeline,
10407
10470
  viewMembers,
10408
- onOpenImageViewer
10471
+ onOpenImageViewer,
10472
+ canEdit,
10473
+ onAddNote: handleOpenNoteForEntry
10409
10474
  }
10410
10475
  )), /* @__PURE__ */ import_react20.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react20.default.createElement("label", { className: "text-xs text-slate-300" }, "Tamanho no Node (Size)"), /* @__PURE__ */ import_react20.default.createElement("div", { className: "flex items-center gap-5" }, ["small", "medium", "large"].map((s) => {
10411
10476
  const isSelected = size === s;
package/dist/index.mjs CHANGED
@@ -9753,12 +9753,16 @@ var EVENT_ICONS = {
9753
9753
  function QuestTimeline({
9754
9754
  timeline = [],
9755
9755
  viewMembers = [],
9756
- onOpenImageViewer
9756
+ onOpenImageViewer,
9757
+ onAddNote,
9758
+ // Nova prop para adicionar nota a uma etapa específica
9759
+ canEdit = true
9757
9760
  }) {
9758
9761
  const sortedTimeline = [...timeline].sort((a, b) => b.timestamp - a.timestamp);
9759
9762
  const getMemberName = (userId) => {
9760
- const member = viewMembers.find((m) => m.id === userId);
9761
- return (member == null ? void 0 : member.name) || (member == null ? void 0 : member.email) || "Sistema";
9763
+ if (!userId) return "Sistema";
9764
+ const member = viewMembers.find((m) => String(m.id) === String(userId));
9765
+ return (member == null ? void 0 : member.name) || (member == null ? void 0 : member.email) || userId;
9762
9766
  };
9763
9767
  const formatDate = (timestamp) => {
9764
9768
  const date = new Date(timestamp);
@@ -9776,7 +9780,19 @@ function QuestTimeline({
9776
9780
  const config = EVENT_ICONS[event.type] || EVENT_ICONS.default;
9777
9781
  const Icon = config.icon;
9778
9782
  const actorName = getMemberName(event.actorId);
9779
- return /* @__PURE__ */ React18.createElement("div", { key: index, className: "relative animate-in fade-in slide-in-from-left-2 duration-300", style: { animationDelay: `${index * 50}ms` } }, /* @__PURE__ */ React18.createElement("div", { className: `absolute -left-[35px] top-0 w-8 h-8 rounded-full ${config.bg} flex items-center justify-center ring-4 ring-slate-950 border border-white/5` }, /* @__PURE__ */ React18.createElement(Icon, { className: config.iconColor || config.color, size: 14 })), /* @__PURE__ */ React18.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React18.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React18.createElement("span", { className: "text-xs font-semibold text-slate-200" }, actorName), /* @__PURE__ */ React18.createElement("span", { className: "text-[10px] text-slate-500 tabular-nums" }, formatDate(event.timestamp))), /* @__PURE__ */ React18.createElement("p", { className: "text-sm text-slate-400 leading-relaxed" }, event.description), (event.from_status || event.to_status) && /* @__PURE__ */ React18.createElement("div", { className: "flex items-center gap-1.5 py-1 px-2 rounded-md bg-white/5 border border-white/5 w-fit mt-2" }, /* @__PURE__ */ React18.createElement("span", { className: "text-[10px] text-slate-500" }, event.from_status || "?"), /* @__PURE__ */ React18.createElement(FiArrowRight, { size: 10, className: "text-slate-600" }), /* @__PURE__ */ React18.createElement("span", { className: "text-[10px] text-indigo-300 font-bold" }, event.to_status || "?")), event.metadata && Object.keys(event.metadata).length > 0 && /* @__PURE__ */ React18.createElement("div", { className: "mt-2 text-[10px] grid grid-cols-2 gap-2 p-2 bg-slate-900/50 rounded border border-white/5" }, Object.entries(event.metadata).map(([key, value]) => /* @__PURE__ */ React18.createElement("div", { key, className: "flex flex-col gap-0.5" }, /* @__PURE__ */ React18.createElement("span", { className: "text-slate-500 capitalize" }, key.replace(/_/g, " ")), /* @__PURE__ */ React18.createElement("span", { className: "text-slate-300 truncate font-mono" }, typeof value === "string" ? value : JSON.stringify(value))))), event.links && event.links.length > 0 && /* @__PURE__ */ React18.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, event.links.map((link, lIdx) => /* @__PURE__ */ React18.createElement(
9783
+ return /* @__PURE__ */ React18.createElement("div", { key: event.id || index, className: "relative animate-in fade-in slide-in-from-left-2 duration-300", style: { animationDelay: `${index * 50}ms` } }, /* @__PURE__ */ React18.createElement("div", { className: `absolute -left-[35px] top-0 w-8 h-8 rounded-full ${config.bg} flex items-center justify-center ring-4 ring-slate-950 border border-white/5` }, /* @__PURE__ */ React18.createElement(Icon, { className: config.iconColor || config.color, size: 14 })), /* @__PURE__ */ React18.createElement("div", { className: "group space-y-1" }, /* @__PURE__ */ React18.createElement("div", { className: "flex items-center justify-between gap-4" }, /* @__PURE__ */ React18.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React18.createElement("span", { className: "text-xs font-semibold text-slate-200" }, actorName), canEdit && onAddNote && /* @__PURE__ */ React18.createElement(
9784
+ "button",
9785
+ {
9786
+ onClick: () => onAddNote(event),
9787
+ className: "opacity-0 group-hover:opacity-100 p-1 rounded-md hover:bg-white/5 text-slate-500 hover:text-indigo-400 transition-all",
9788
+ title: "Adicionar detalhe/anexo a esta etapa"
9789
+ },
9790
+ /* @__PURE__ */ React18.createElement(FiPlusCircle, { size: 12 })
9791
+ )), /* @__PURE__ */ React18.createElement("span", { className: "text-[10px] text-slate-500 tabular-nums" }, formatDate(event.timestamp))), /* @__PURE__ */ React18.createElement("p", { className: "text-sm text-slate-400 leading-relaxed" }, event.description), event.notes && /* @__PURE__ */ React18.createElement("div", { className: "mt-1 py-1 px-2 rounded bg-indigo-500/5 border-l-2 border-indigo-500/30" }, /* @__PURE__ */ React18.createElement("p", { className: "text-[11px] text-slate-300 italic whitespace-pre-wrap" }, event.notes)), (event.from_status || event.to_status) && /* @__PURE__ */ React18.createElement("div", { className: "flex items-center gap-1.5 py-1 px-2 rounded-md bg-white/5 border border-white/5 w-fit mt-2" }, /* @__PURE__ */ React18.createElement("span", { className: "text-[10px] text-slate-500" }, event.from_status || "?"), /* @__PURE__ */ React18.createElement(FiArrowRight, { size: 10, className: "text-slate-600" }), /* @__PURE__ */ React18.createElement("span", { className: "text-[10px] text-indigo-300 font-bold" }, event.to_status || "?")), event.metadata && Object.keys(event.metadata).length > 0 && /* @__PURE__ */ React18.createElement("div", { className: "mt-2 text-[10px] grid grid-cols-2 gap-2 p-2 bg-slate-900/50 rounded border border-white/5" }, Object.entries(event.metadata).map(([key, value]) => {
9792
+ const isAssigneeId = key.toLowerCase().includes("assignee_id") || key.toLowerCase().includes("respons\xE1vel") || key.toLowerCase().includes("assignee id");
9793
+ const displayValue = isAssigneeId && typeof value === "string" ? getMemberName(value) : typeof value === "string" ? value : JSON.stringify(value);
9794
+ return /* @__PURE__ */ React18.createElement("div", { key, className: "flex flex-col gap-0.5" }, /* @__PURE__ */ React18.createElement("span", { className: "text-slate-500 capitalize" }, key.replace(/_/g, " ")), /* @__PURE__ */ React18.createElement("span", { className: `${isAssigneeId ? "text-indigo-300 font-medium" : "text-slate-300"} truncate font-mono` }, displayValue));
9795
+ })), event.links && event.links.length > 0 && /* @__PURE__ */ React18.createElement("div", { className: "flex flex-wrap gap-2 mt-2" }, event.links.map((link, lIdx) => /* @__PURE__ */ React18.createElement(
9780
9796
  "a",
9781
9797
  {
9782
9798
  key: lIdx,
@@ -9793,10 +9809,10 @@ function QuestTimeline({
9793
9809
  key: iIdx,
9794
9810
  type: "button",
9795
9811
  onClick: () => onOpenImageViewer == null ? void 0 : onOpenImageViewer(event.images, iIdx),
9796
- className: "relative w-12 h-12 rounded border border-white/10 overflow-hidden hover:border-sky-500/50 transition-colors group"
9812
+ className: "relative w-12 h-12 rounded border border-white/10 overflow-hidden hover:border-sky-500/50 transition-colors group/img"
9797
9813
  },
9798
9814
  /* @__PURE__ */ React18.createElement("img", { src: img.value, alt: img.name, className: "w-full h-full object-cover" }),
9799
- /* @__PURE__ */ React18.createElement("div", { className: "absolute inset-0 bg-slate-900/40 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-opacity" }, /* @__PURE__ */ React18.createElement(FiSearch6, { size: 12, className: "text-white" }))
9815
+ /* @__PURE__ */ React18.createElement("div", { className: "absolute inset-0 bg-slate-900/40 opacity-0 group-hover/img:opacity-100 flex items-center justify-center transition-opacity" }, /* @__PURE__ */ React18.createElement(FiSearch6, { size: 12, className: "text-white" }))
9800
9816
  )))));
9801
9817
  })));
9802
9818
  }
@@ -9862,6 +9878,8 @@ function QuestDetailsPanel({
9862
9878
  const [noteLinks, setNoteLinks] = useState19([]);
9863
9879
  const [noteImages, setNoteImages] = useState19([]);
9864
9880
  const [isNoteFormOpen, setIsNoteFormOpen] = useState19(false);
9881
+ const [editingEntryId, setEditingEntryId] = useState19(null);
9882
+ const [editingEntryLabel, setEditingEntryLabel] = useState19("");
9865
9883
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
9866
9884
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
9867
9885
  initialWidth: isReadMode ? 700 : 440,
@@ -9993,14 +10011,30 @@ function QuestDetailsPanel({
9993
10011
  }
9994
10012
  };
9995
10013
  const handleAddTimelineNote = async () => {
9996
- if (!noteText.trim() || isSaving) return;
10014
+ if (!noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving) return;
9997
10015
  setIsSaving(true);
9998
10016
  const actorId = (currentUser == null ? void 0 : currentUser.id) || "unknown";
9999
- const newEntry = createTimelineEntry(actorId, noteText, "comment", {
10000
- links: noteLinks,
10001
- images: noteImages
10002
- });
10003
- const updatedTimeline = [...timeline, newEntry];
10017
+ let updatedTimeline;
10018
+ if (editingEntryId) {
10019
+ updatedTimeline = timeline.map((entry) => {
10020
+ if (entry.id === editingEntryId) {
10021
+ return {
10022
+ ...entry,
10023
+ notes: noteText.trim() ? entry.notes ? `${entry.notes}
10024
+ ${noteText}` : noteText : entry.notes,
10025
+ links: [...entry.links || [], ...noteLinks],
10026
+ images: [...entry.images || [], ...noteImages]
10027
+ };
10028
+ }
10029
+ return entry;
10030
+ });
10031
+ } else {
10032
+ const newEntry = createTimelineEntry(actorId, noteText, "comment", {
10033
+ links: noteLinks,
10034
+ images: noteImages
10035
+ });
10036
+ updatedTimeline = [...timeline, newEntry];
10037
+ }
10004
10038
  try {
10005
10039
  const nodeToSave = {
10006
10040
  ...node,
@@ -10013,12 +10047,26 @@ function QuestDetailsPanel({
10013
10047
  setNoteLinks([]);
10014
10048
  setNoteImages([]);
10015
10049
  setIsNoteFormOpen(false);
10050
+ setEditingEntryId(null);
10051
+ setEditingEntryLabel("");
10016
10052
  } catch (error) {
10017
10053
  console.error("Erro ao adicionar nota:", error);
10018
10054
  } finally {
10019
10055
  setIsSaving(false);
10020
10056
  }
10021
10057
  };
10058
+ const handleOpenNoteForEntry = (entry) => {
10059
+ setEditingEntryId(entry.id);
10060
+ setEditingEntryLabel(entry.description);
10061
+ setNoteText("");
10062
+ setNoteLinks([]);
10063
+ setNoteImages([]);
10064
+ setIsNoteFormOpen(true);
10065
+ setTimeout(() => {
10066
+ const form = document.getElementById("quest-note-form");
10067
+ form == null ? void 0 : form.scrollIntoView({ behavior: "smooth", block: "center" });
10068
+ }, 100);
10069
+ };
10022
10070
  const addLinkToNote = () => {
10023
10071
  setNoteLinks([...noteLinks, { id: v4_default(), name: "", value: "" }]);
10024
10072
  };
@@ -10380,17 +10428,32 @@ function QuestDetailsPanel({
10380
10428
  "button",
10381
10429
  {
10382
10430
  type: "button",
10383
- onClick: () => setIsNoteFormOpen(!isNoteFormOpen),
10431
+ onClick: () => {
10432
+ if (isNoteFormOpen && editingEntryId) {
10433
+ setEditingEntryId(null);
10434
+ setEditingEntryLabel("");
10435
+ setNoteText("");
10436
+ setNoteLinks([]);
10437
+ setNoteImages([]);
10438
+ } else {
10439
+ setIsNoteFormOpen(!isNoteFormOpen);
10440
+ setEditingEntryId(null);
10441
+ setEditingEntryLabel("");
10442
+ }
10443
+ },
10384
10444
  className: "flex items-center gap-1.5 px-2 py-1 text-[10px] rounded-md bg-white/5 hover:bg-white/10 border border-white/10 transition-colors text-slate-300"
10385
10445
  },
10386
- isNoteFormOpen ? /* @__PURE__ */ React19.createElement(FiX7, { size: 12 }) : /* @__PURE__ */ React19.createElement(FiPlus8, { size: 12 }),
10387
- isNoteFormOpen ? "Cancelar" : "Nova Nota"
10388
- )), isNoteFormOpen && /* @__PURE__ */ React19.createElement("div", { className: "mb-6 p-4 rounded-xl bg-white/5 border border-white/5 space-y-3 animate-in fade-in slide-in-from-top-2" }, /* @__PURE__ */ React19.createElement(
10446
+ isNoteFormOpen && !editingEntryId ? /* @__PURE__ */ React19.createElement(FiX7, { size: 12 }) : /* @__PURE__ */ React19.createElement(FiPlus8, { size: 12 }),
10447
+ isNoteFormOpen && !editingEntryId ? "Cancelar" : "Nova Nota"
10448
+ )), isNoteFormOpen && /* @__PURE__ */ React19.createElement("div", { id: "quest-note-form", className: "mb-6 p-4 rounded-xl bg-white/5 border border-white/5 space-y-3 animate-in fade-in slide-in-from-top-2 border-l-4 border-l-indigo-500/40" }, editingEntryId && /* @__PURE__ */ React19.createElement("div", { className: "flex items-center justify-between mb-2 pb-2 border-b border-white/5" }, /* @__PURE__ */ React19.createElement("div", { className: "flex flex-col" }, /* @__PURE__ */ React19.createElement("span", { className: "text-[10px] text-indigo-400 font-bold uppercase tracking-wider" }, "Adicionando \xE0 etapa:"), /* @__PURE__ */ React19.createElement("span", { className: "text-xs text-slate-300 italic truncate max-w-[250px]" }, editingEntryLabel)), /* @__PURE__ */ React19.createElement("button", { type: "button", onClick: () => {
10449
+ setIsNoteFormOpen(false);
10450
+ setEditingEntryId(null);
10451
+ }, className: "text-slate-500 hover:text-white" }, /* @__PURE__ */ React19.createElement(FiX7, { size: 14 }))), /* @__PURE__ */ React19.createElement(
10389
10452
  "textarea",
10390
10453
  {
10391
10454
  value: noteText,
10392
10455
  onChange: (e) => setNoteText(e.target.value),
10393
- placeholder: "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10456
+ placeholder: editingEntryId ? "Adicione uma observa\xE7\xE3o a esta etapa..." : "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10394
10457
  className: "w-full bg-slate-900/50 p-3 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-1 focus:ring-indigo-500/50 min-h-[80px] resize-none"
10395
10458
  }
10396
10459
  ), /* @__PURE__ */ React19.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React19.createElement("span", { className: "text-[10px] text-slate-500 uppercase font-bold tracking-tight" }, "Links"), /* @__PURE__ */ React19.createElement("button", { type: "button", onClick: addLinkToNote, className: "text-indigo-400 hover:text-indigo-300 text-[10px] flex items-center gap-1 font-medium" }, /* @__PURE__ */ React19.createElement(FiPlus8, { size: 10 }), " Adicionar Link")), noteLinks.length > 0 && /* @__PURE__ */ React19.createElement("div", { className: "space-y-2" }, noteLinks.map((link) => /* @__PURE__ */ React19.createElement("div", { key: link.id, className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-1" }, /* @__PURE__ */ React19.createElement(
@@ -10425,16 +10488,18 @@ function QuestDetailsPanel({
10425
10488
  {
10426
10489
  type: "button",
10427
10490
  onClick: handleAddTimelineNote,
10428
- disabled: !noteText.trim() || isSaving,
10491
+ disabled: !noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving,
10429
10492
  className: "px-3 py-1.5 rounded-lg bg-indigo-600 hover:bg-indigo-500 text-white text-xs font-semibold transition-colors disabled:opacity-50"
10430
10493
  },
10431
- "Adicionar ao Hist\xF3rico"
10494
+ editingEntryId ? "Salvar nesta Etapa" : "Adicionar ao Hist\xF3rico"
10432
10495
  ))), /* @__PURE__ */ React19.createElement(
10433
10496
  QuestTimeline,
10434
10497
  {
10435
10498
  timeline,
10436
10499
  viewMembers,
10437
- onOpenImageViewer
10500
+ onOpenImageViewer,
10501
+ canEdit,
10502
+ onAddNote: handleOpenNoteForEntry
10438
10503
  }
10439
10504
  )), /* @__PURE__ */ React19.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Tamanho no Node (Size)"), /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-5" }, ["small", "medium", "large"].map((s) => {
10440
10505
  const isSelected = size === s;
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.32",
3
+ "version": "1.2.5-dev.33",
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",