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

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.
Files changed (3) hide show
  1. package/dist/index.js +122 -76
  2. package/dist/index.mjs +122 -76
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8843,7 +8843,6 @@ function InSceneQuestForm({
8843
8843
  }
8844
8844
  };
8845
8845
  const handleSubmit = (e) => {
8846
- var _a2, _b2, _c, _d;
8847
8846
  e.preventDefault();
8848
8847
  if (!name.trim()) {
8849
8848
  alert("O campo 'T\xEDtulo' \xE9 obrigat\xF3rio.");
@@ -8853,21 +8852,7 @@ function InSceneQuestForm({
8853
8852
  customProps.filter((prop) => prop.key.trim() && !prop.isEditing)
8854
8853
  );
8855
8854
  const processedSections = processDescriptionForSave(description, []);
8856
- const initialTimeline = [
8857
- createTimelineEntry(
8858
- ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id) || "unknown",
8859
- `Quest criada por ${((_b2 = session == null ? void 0 : session.user) == null ? void 0 : _b2.name) || "usu\xE1rio"}.`,
8860
- "quest_created"
8861
- )
8862
- ];
8863
- if (assigneeId) {
8864
- initialTimeline.push(createTimelineEntry(
8865
- ((_c = session == null ? void 0 : session.user) == null ? void 0 : _c.id) || "unknown",
8866
- `Quest atribu\xEDda para ${((_d = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _d.name) || "respons\xE1vel"}.`,
8867
- "assignee_changed",
8868
- { metadata: { to_assignee_id: assigneeId } }
8869
- ));
8870
- }
8855
+ const initialTimeline = [];
8871
8856
  onSave({
8872
8857
  name: standardizedName,
8873
8858
  // SALVA O NOME FORMATADO
@@ -9711,25 +9696,20 @@ var import_fi18 = require("react-icons/fi");
9711
9696
  // src/components/QuestTimeline.jsx
9712
9697
  var import_react19 = __toESM(require("react"));
9713
9698
  var import_fi17 = require("react-icons/fi");
9714
- var EVENT_ICONS = {
9715
- "quest_created": { icon: import_fi17.FiPlusCircle, color: "text-sky-400", bg: "bg-sky-400/10" },
9716
- "assignee_changed": { icon: import_fi17.FiUserPlus, color: "text-indigo-400", bg: "bg-indigo-400/10" },
9717
- "status_changed": { icon: import_fi17.FiRotateCcw, color: "text-amber-400", bg: "bg-amber-400/10" },
9718
- "review_cycle_started": { icon: import_fi17.FiSearch, color: "text-purple-400", bg: "bg-purple-400/10" },
9719
- "review_completed": { icon: import_fi17.FiCheckSquare, color: "text-emerald-400", bg: "bg-emerald-400/10" },
9720
- "comment": { icon: import_fi17.FiMessageSquare, color: "text-slate-400", bg: "bg-slate-400/10" },
9721
- "quest_finished": { icon: import_fi17.FiTarget, color: "text-green-400", bg: "bg-green-400/10" },
9722
- "default": { icon: import_fi17.FiGitCommit, color: "text-slate-500", bg: "bg-slate-500/10" }
9723
- };
9699
+ var DEFAULT_ICON_CONFIG = { icon: import_fi17.FiGitCommit, color: "text-indigo-400", bg: "bg-indigo-400/10" };
9724
9700
  function QuestTimeline({
9725
9701
  timeline = [],
9726
9702
  viewMembers = [],
9727
- onOpenImageViewer
9703
+ onOpenImageViewer,
9704
+ onAddNote,
9705
+ // Nova prop para adicionar nota a uma etapa específica
9706
+ canEdit = true
9728
9707
  }) {
9729
9708
  const sortedTimeline = [...timeline].sort((a, b) => b.timestamp - a.timestamp);
9730
9709
  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";
9710
+ if (!userId) return "Sistema";
9711
+ const member = viewMembers.find((m) => String(m.id) === String(userId));
9712
+ return (member == null ? void 0 : member.name) || (member == null ? void 0 : member.email) || userId;
9733
9713
  };
9734
9714
  const formatDate = (timestamp) => {
9735
9715
  const date = new Date(timestamp);
@@ -9744,10 +9724,22 @@ function QuestTimeline({
9744
9724
  return /* @__PURE__ */ import_react19.default.createElement("div", { className: "py-6 flex flex-col items-center justify-center gap-2 border border-white/5 bg-white/5 rounded-xl" }, /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiAlertCircle, { className: "text-slate-600", size: 16 }), /* @__PURE__ */ import_react19.default.createElement("p", { className: "text-[10px] text-slate-500 font-light uppercase tracking-widest" }, "Nenhum hist\xF3rico dispon\xEDvel"));
9745
9725
  }
9746
9726
  return /* @__PURE__ */ import_react19.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ import_react19.default.createElement("h3", { className: "text-xs text-slate-400 font-medium uppercase tracking-wider flex items-center gap-2" }, "Hist\xF3rico (Timeline)"), /* @__PURE__ */ import_react19.default.createElement("div", { className: "relative pl-10 space-y-6 before:content-[''] before:absolute before:left-[21px] before:top-2 before:bottom-2 before:w-[2px] before:bg-white/5" }, sortedTimeline.map((event, index) => {
9747
- const config = EVENT_ICONS[event.type] || EVENT_ICONS.default;
9727
+ const config = DEFAULT_ICON_CONFIG;
9748
9728
  const Icon = config.icon;
9749
9729
  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(
9730
+ 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(
9731
+ "button",
9732
+ {
9733
+ onClick: () => onAddNote(event),
9734
+ className: "opacity-0 group-hover:opacity-100 p-1 rounded-md hover:bg-white/5 text-slate-500 hover:text-indigo-400 transition-all",
9735
+ title: "Adicionar detalhe/anexo a esta etapa"
9736
+ },
9737
+ /* @__PURE__ */ import_react19.default.createElement(import_fi17.FiPlusCircle, { size: 12 })
9738
+ )), /* @__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]) => {
9739
+ const isAssigneeId = key.toLowerCase().includes("assignee_id") || key.toLowerCase().includes("respons\xE1vel") || key.toLowerCase().includes("assignee id");
9740
+ const displayValue = isAssigneeId && typeof value === "string" ? getMemberName(value) : typeof value === "string" ? value : JSON.stringify(value);
9741
+ 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));
9742
+ })), 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
9743
  "a",
9752
9744
  {
9753
9745
  key: lIdx,
@@ -9764,10 +9756,10 @@ function QuestTimeline({
9764
9756
  key: iIdx,
9765
9757
  type: "button",
9766
9758
  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"
9759
+ className: "relative w-12 h-12 rounded border border-white/10 overflow-hidden hover:border-sky-500/50 transition-colors group/img"
9768
9760
  },
9769
9761
  /* @__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" }))
9762
+ /* @__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
9763
  )))));
9772
9764
  })));
9773
9765
  }
@@ -9833,6 +9825,8 @@ function QuestDetailsPanel({
9833
9825
  const [noteLinks, setNoteLinks] = (0, import_react20.useState)([]);
9834
9826
  const [noteImages, setNoteImages] = (0, import_react20.useState)([]);
9835
9827
  const [isNoteFormOpen, setIsNoteFormOpen] = (0, import_react20.useState)(false);
9828
+ const [editingEntryId, setEditingEntryId] = (0, import_react20.useState)(null);
9829
+ const [editingEntryLabel, setEditingEntryLabel] = (0, import_react20.useState)("");
9836
9830
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
9837
9831
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
9838
9832
  initialWidth: isReadMode ? 700 : 440,
@@ -9923,12 +9917,11 @@ function QuestDetailsPanel({
9923
9917
  const actorName = (currentUser == null ? void 0 : currentUser.name) || "Revisor";
9924
9918
  const targetMember = viewMembers.find((m) => String(m.id) === String(returnAssigneeId));
9925
9919
  const targetName = (targetMember == null ? void 0 : targetMember.name) || "executor";
9926
- const newEvent = {
9927
- type: "review_returned",
9928
- timestamp: Date.now(),
9920
+ const newEvent = createTimelineEntry(
9929
9921
  actorId,
9930
- description: `Review finalizada com retorno para opera\xE7\xE3o por ${actorName}. Novo respons\xE1vel: ${targetName}.`
9931
- };
9922
+ `Status "In Progress" - Respons\xE1vel: ${targetName}`,
9923
+ "status_changed"
9924
+ );
9932
9925
  const updatedTimeline = [...timeline, newEvent];
9933
9926
  const updatedStatus = "In Progress";
9934
9927
  const updatedColor = "#eab308";
@@ -9964,14 +9957,30 @@ function QuestDetailsPanel({
9964
9957
  }
9965
9958
  };
9966
9959
  const handleAddTimelineNote = async () => {
9967
- if (!noteText.trim() || isSaving) return;
9960
+ if (!noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving) return;
9968
9961
  setIsSaving(true);
9969
9962
  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];
9963
+ let updatedTimeline;
9964
+ if (editingEntryId) {
9965
+ updatedTimeline = timeline.map((entry) => {
9966
+ if (entry.id === editingEntryId) {
9967
+ return {
9968
+ ...entry,
9969
+ notes: noteText.trim() ? entry.notes ? `${entry.notes}
9970
+ ${noteText}` : noteText : entry.notes,
9971
+ links: [...entry.links || [], ...noteLinks],
9972
+ images: [...entry.images || [], ...noteImages]
9973
+ };
9974
+ }
9975
+ return entry;
9976
+ });
9977
+ } else {
9978
+ const newEntry = createTimelineEntry(actorId, noteText, "comment", {
9979
+ links: noteLinks,
9980
+ images: noteImages
9981
+ });
9982
+ updatedTimeline = [...timeline, newEntry];
9983
+ }
9975
9984
  try {
9976
9985
  const nodeToSave = {
9977
9986
  ...node,
@@ -9984,12 +9993,26 @@ function QuestDetailsPanel({
9984
9993
  setNoteLinks([]);
9985
9994
  setNoteImages([]);
9986
9995
  setIsNoteFormOpen(false);
9996
+ setEditingEntryId(null);
9997
+ setEditingEntryLabel("");
9987
9998
  } catch (error) {
9988
9999
  console.error("Erro ao adicionar nota:", error);
9989
10000
  } finally {
9990
10001
  setIsSaving(false);
9991
10002
  }
9992
10003
  };
10004
+ const handleOpenNoteForEntry = (entry) => {
10005
+ setEditingEntryId(entry.id);
10006
+ setEditingEntryLabel(entry.description);
10007
+ setNoteText("");
10008
+ setNoteLinks([]);
10009
+ setNoteImages([]);
10010
+ setIsNoteFormOpen(true);
10011
+ setTimeout(() => {
10012
+ const form = document.getElementById("quest-note-form");
10013
+ form == null ? void 0 : form.scrollIntoView({ behavior: "smooth", block: "center" });
10014
+ }, 100);
10015
+ };
9993
10016
  const addLinkToNote = () => {
9994
10017
  setNoteLinks([...noteLinks, { id: v4_default(), name: "", value: "" }]);
9995
10018
  };
@@ -10011,8 +10034,18 @@ function QuestDetailsPanel({
10011
10034
  const handleStatusChange = (newStatus) => {
10012
10035
  setStatus(newStatus);
10013
10036
  const newColor = QUEST_STATUS_COLORS3[newStatus];
10037
+ const actorId = (currentUser == null ? void 0 : currentUser.id) || "unknown";
10038
+ const currentAssignee = viewMembers.find((m) => String(m.id) === String(assigneeId));
10039
+ const assigneeName = (currentAssignee == null ? void 0 : currentAssignee.name) || (currentAssignee == null ? void 0 : currentAssignee.email) || "Nenhum";
10040
+ const newEvent = createTimelineEntry(
10041
+ actorId,
10042
+ `Status "${newStatus}" - Respons\xE1vel: ${assigneeName}`,
10043
+ "status_changed"
10044
+ );
10045
+ const newTimeline = [...timeline, newEvent];
10046
+ setTimeline(newTimeline);
10014
10047
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
10015
- onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
10048
+ onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor, timeline: newTimeline });
10016
10049
  setHasUnsavedChanges(true);
10017
10050
  };
10018
10051
  const handleAddType = (newType) => {
@@ -10233,13 +10266,11 @@ function QuestDetailsPanel({
10233
10266
  {
10234
10267
  onClick: () => {
10235
10268
  setAssigneeId("");
10236
- const newTimeline = [...timeline, {
10237
- type: "assignee_changed",
10238
- timestamp: Date.now(),
10239
- actorId: "user",
10240
- to_assignee_id: null,
10241
- description: `Respons\xE1vel removido.`
10242
- }];
10269
+ const newTimeline = [...timeline, createTimelineEntry(
10270
+ (currentUser == null ? void 0 : currentUser.id) || "unknown",
10271
+ `Status "${status}" - Respons\xE1vel: Nenhum`,
10272
+ "status_changed"
10273
+ )];
10243
10274
  setTimeline(newTimeline);
10244
10275
  setHasUnsavedChanges(true);
10245
10276
  setIsAssigneeDropdownOpen(false);
@@ -10267,13 +10298,11 @@ function QuestDetailsPanel({
10267
10298
  key: member.id,
10268
10299
  onClick: () => {
10269
10300
  setAssigneeId(member.id);
10270
- const newTimeline = [...timeline, {
10271
- type: "assignee_changed",
10272
- timestamp: Date.now(),
10273
- actorId: "user",
10274
- to_assignee_id: member.id,
10275
- description: `Quest atribu\xEDda para ${member.name || member.email}.`
10276
- }];
10301
+ const newTimeline = [...timeline, createTimelineEntry(
10302
+ (currentUser == null ? void 0 : currentUser.id) || "unknown",
10303
+ `Status "${status}" - Respons\xE1vel: ${member.name || member.email}`,
10304
+ "status_changed"
10305
+ )];
10277
10306
  setTimeline(newTimeline);
10278
10307
  setHasUnsavedChanges(true);
10279
10308
  setIsAssigneeDropdownOpen(false);
@@ -10351,17 +10380,32 @@ function QuestDetailsPanel({
10351
10380
  "button",
10352
10381
  {
10353
10382
  type: "button",
10354
- onClick: () => setIsNoteFormOpen(!isNoteFormOpen),
10383
+ onClick: () => {
10384
+ if (isNoteFormOpen && editingEntryId) {
10385
+ setEditingEntryId(null);
10386
+ setEditingEntryLabel("");
10387
+ setNoteText("");
10388
+ setNoteLinks([]);
10389
+ setNoteImages([]);
10390
+ } else {
10391
+ setIsNoteFormOpen(!isNoteFormOpen);
10392
+ setEditingEntryId(null);
10393
+ setEditingEntryLabel("");
10394
+ }
10395
+ },
10355
10396
  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
10397
  },
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(
10398
+ isNoteFormOpen && !editingEntryId ? /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiX, { size: 12 }) : /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiPlus, { size: 12 }),
10399
+ isNoteFormOpen && !editingEntryId ? "Cancelar" : "Nova Nota"
10400
+ )), 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: () => {
10401
+ setIsNoteFormOpen(false);
10402
+ setEditingEntryId(null);
10403
+ }, className: "text-slate-500 hover:text-white" }, /* @__PURE__ */ import_react20.default.createElement(import_fi18.FiX, { size: 14 }))), /* @__PURE__ */ import_react20.default.createElement(
10360
10404
  "textarea",
10361
10405
  {
10362
10406
  value: noteText,
10363
10407
  onChange: (e) => setNoteText(e.target.value),
10364
- placeholder: "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10408
+ placeholder: editingEntryId ? "Adicione uma observa\xE7\xE3o a esta etapa..." : "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10365
10409
  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
10410
  }
10367
10411
  ), /* @__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 +10440,18 @@ function QuestDetailsPanel({
10396
10440
  {
10397
10441
  type: "button",
10398
10442
  onClick: handleAddTimelineNote,
10399
- disabled: !noteText.trim() || isSaving,
10443
+ disabled: !noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving,
10400
10444
  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
10445
  },
10402
- "Adicionar ao Hist\xF3rico"
10446
+ editingEntryId ? "Salvar nesta Etapa" : "Adicionar ao Hist\xF3rico"
10403
10447
  ))), /* @__PURE__ */ import_react20.default.createElement(
10404
10448
  QuestTimeline,
10405
10449
  {
10406
10450
  timeline,
10407
10451
  viewMembers,
10408
- onOpenImageViewer
10452
+ onOpenImageViewer,
10453
+ canEdit,
10454
+ onAddNote: handleOpenNoteForEntry
10409
10455
  }
10410
10456
  )), /* @__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
10457
  const isSelected = size === s;
@@ -13961,7 +14007,7 @@ function XViewScene({
13961
14007
  } = questNode;
13962
14008
  const newTimeline = [...cleanQuestNode.timeline || [], createTimelineEntry(
13963
14009
  ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id) || "unknown",
13964
- `Quest iniciada ("In Progress") por ${((_b2 = session == null ? void 0 : session.user) == null ? void 0 : _b2.name) || "usu\xE1rio"}.`,
14010
+ `Status "In Progress" - Respons\xE1vel: ${((_b2 = session == null ? void 0 : session.user) == null ? void 0 : _b2.name) || "usu\xE1rio"}`,
13965
14011
  "status_changed"
13966
14012
  )];
13967
14013
  const updatedNode = {
@@ -13999,15 +14045,15 @@ function XViewScene({
13999
14045
  nextIdxToAssign = currentReviewerIdx + 1;
14000
14046
  newTimeline.push(createTimelineEntry(
14001
14047
  ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id) || cleanQuestNode.assignee_id,
14002
- `Review conclu\xEDda por ${((_b2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _b2.name) || "revisor"}.`,
14003
- "review_completed"
14048
+ `Status "Review" - Conclu\xEDda por ${((_b2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _b2.name) || "revisor"}`,
14049
+ "status_changed"
14004
14050
  ));
14005
14051
  } else {
14006
14052
  nextIdxToAssign = currentReviewerIdx !== -1 ? currentReviewerIdx : reviewStack.length;
14007
14053
  newTimeline.push(createTimelineEntry(
14008
14054
  ((_c2 = session == null ? void 0 : session.user) == null ? void 0 : _c2.id) || cleanQuestNode.assignee_id,
14009
- `Tarefa principal entregue por ${((_d2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _d2.name) || ((_e2 = session == null ? void 0 : session.user) == null ? void 0 : _e2.name) || "respons\xE1vel"}.`,
14010
- "task_completed"
14055
+ `Status "Done" - Finalizado por ${((_d2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _d2.name) || ((_e2 = session == null ? void 0 : session.user) == null ? void 0 : _e2.name) || "respons\xE1vel"}`,
14056
+ "status_changed"
14011
14057
  ));
14012
14058
  }
14013
14059
  if (nextIdxToAssign < reviewStack.length) {
@@ -14019,8 +14065,8 @@ function XViewScene({
14019
14065
  nextStatus = "Review";
14020
14066
  newTimeline.push(createTimelineEntry(
14021
14067
  "system",
14022
- `Quest atribu\xEDda para revis\xE3o a ${((_f2 = viewMembers.find((m) => m.id === nextAssigneeId)) == null ? void 0 : _f2.name) || "revisor"}.`,
14023
- "assignee_changed",
14068
+ `Status "Review" - Respons\xE1vel: ${((_f2 = viewMembers.find((m) => m.id === nextAssigneeId)) == null ? void 0 : _f2.name) || "revisor"}`,
14069
+ "status_changed",
14024
14070
  { metadata: { to_assignee_id: nextAssigneeId } }
14025
14071
  ));
14026
14072
  } else {
@@ -14035,8 +14081,8 @@ function XViewScene({
14035
14081
  if (nextStatus === "Done") {
14036
14082
  newTimeline.push(createTimelineEntry(
14037
14083
  "system",
14038
- `Quest finalizada!`,
14039
- "quest_finished"
14084
+ `Status "Done" - Quest Finalizada`,
14085
+ "status_changed"
14040
14086
  ));
14041
14087
  }
14042
14088
  const updatedNode = {
package/dist/index.mjs CHANGED
@@ -8860,7 +8860,6 @@ function InSceneQuestForm({
8860
8860
  }
8861
8861
  };
8862
8862
  const handleSubmit = (e) => {
8863
- var _a2, _b2, _c, _d;
8864
8863
  e.preventDefault();
8865
8864
  if (!name.trim()) {
8866
8865
  alert("O campo 'T\xEDtulo' \xE9 obrigat\xF3rio.");
@@ -8870,21 +8869,7 @@ function InSceneQuestForm({
8870
8869
  customProps.filter((prop) => prop.key.trim() && !prop.isEditing)
8871
8870
  );
8872
8871
  const processedSections = processDescriptionForSave(description, []);
8873
- const initialTimeline = [
8874
- createTimelineEntry(
8875
- ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id) || "unknown",
8876
- `Quest criada por ${((_b2 = session == null ? void 0 : session.user) == null ? void 0 : _b2.name) || "usu\xE1rio"}.`,
8877
- "quest_created"
8878
- )
8879
- ];
8880
- if (assigneeId) {
8881
- initialTimeline.push(createTimelineEntry(
8882
- ((_c = session == null ? void 0 : session.user) == null ? void 0 : _c.id) || "unknown",
8883
- `Quest atribu\xEDda para ${((_d = viewMembers.find((m) => m.id === assigneeId)) == null ? void 0 : _d.name) || "respons\xE1vel"}.`,
8884
- "assignee_changed",
8885
- { metadata: { to_assignee_id: assigneeId } }
8886
- ));
8887
- }
8872
+ const initialTimeline = [];
8888
8873
  onSave({
8889
8874
  name: standardizedName,
8890
8875
  // SALVA O NOME FORMATADO
@@ -9740,25 +9725,20 @@ import {
9740
9725
  FiSearch as FiSearch6,
9741
9726
  FiLink as FiLink6
9742
9727
  } from "react-icons/fi";
9743
- var EVENT_ICONS = {
9744
- "quest_created": { icon: FiPlusCircle, color: "text-sky-400", bg: "bg-sky-400/10" },
9745
- "assignee_changed": { icon: FiUserPlus, color: "text-indigo-400", bg: "bg-indigo-400/10" },
9746
- "status_changed": { icon: FiRotateCcw, color: "text-amber-400", bg: "bg-amber-400/10" },
9747
- "review_cycle_started": { icon: FiSearch6, color: "text-purple-400", bg: "bg-purple-400/10" },
9748
- "review_completed": { icon: FiCheckSquare2, color: "text-emerald-400", bg: "bg-emerald-400/10" },
9749
- "comment": { icon: FiMessageSquare, color: "text-slate-400", bg: "bg-slate-400/10" },
9750
- "quest_finished": { icon: FiTarget2, color: "text-green-400", bg: "bg-green-400/10" },
9751
- "default": { icon: FiGitCommit, color: "text-slate-500", bg: "bg-slate-500/10" }
9752
- };
9728
+ var DEFAULT_ICON_CONFIG = { icon: FiGitCommit, color: "text-indigo-400", bg: "bg-indigo-400/10" };
9753
9729
  function QuestTimeline({
9754
9730
  timeline = [],
9755
9731
  viewMembers = [],
9756
- onOpenImageViewer
9732
+ onOpenImageViewer,
9733
+ onAddNote,
9734
+ // Nova prop para adicionar nota a uma etapa específica
9735
+ canEdit = true
9757
9736
  }) {
9758
9737
  const sortedTimeline = [...timeline].sort((a, b) => b.timestamp - a.timestamp);
9759
9738
  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";
9739
+ if (!userId) return "Sistema";
9740
+ const member = viewMembers.find((m) => String(m.id) === String(userId));
9741
+ return (member == null ? void 0 : member.name) || (member == null ? void 0 : member.email) || userId;
9762
9742
  };
9763
9743
  const formatDate = (timestamp) => {
9764
9744
  const date = new Date(timestamp);
@@ -9773,10 +9753,22 @@ function QuestTimeline({
9773
9753
  return /* @__PURE__ */ React18.createElement("div", { className: "py-6 flex flex-col items-center justify-center gap-2 border border-white/5 bg-white/5 rounded-xl" }, /* @__PURE__ */ React18.createElement(FiAlertCircle, { className: "text-slate-600", size: 16 }), /* @__PURE__ */ React18.createElement("p", { className: "text-[10px] text-slate-500 font-light uppercase tracking-widest" }, "Nenhum hist\xF3rico dispon\xEDvel"));
9774
9754
  }
9775
9755
  return /* @__PURE__ */ React18.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React18.createElement("h3", { className: "text-xs text-slate-400 font-medium uppercase tracking-wider flex items-center gap-2" }, "Hist\xF3rico (Timeline)"), /* @__PURE__ */ React18.createElement("div", { className: "relative pl-10 space-y-6 before:content-[''] before:absolute before:left-[21px] before:top-2 before:bottom-2 before:w-[2px] before:bg-white/5" }, sortedTimeline.map((event, index) => {
9776
- const config = EVENT_ICONS[event.type] || EVENT_ICONS.default;
9756
+ const config = DEFAULT_ICON_CONFIG;
9777
9757
  const Icon = config.icon;
9778
9758
  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(
9759
+ 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(
9760
+ "button",
9761
+ {
9762
+ onClick: () => onAddNote(event),
9763
+ className: "opacity-0 group-hover:opacity-100 p-1 rounded-md hover:bg-white/5 text-slate-500 hover:text-indigo-400 transition-all",
9764
+ title: "Adicionar detalhe/anexo a esta etapa"
9765
+ },
9766
+ /* @__PURE__ */ React18.createElement(FiPlusCircle, { size: 12 })
9767
+ )), /* @__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]) => {
9768
+ const isAssigneeId = key.toLowerCase().includes("assignee_id") || key.toLowerCase().includes("respons\xE1vel") || key.toLowerCase().includes("assignee id");
9769
+ const displayValue = isAssigneeId && typeof value === "string" ? getMemberName(value) : typeof value === "string" ? value : JSON.stringify(value);
9770
+ 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));
9771
+ })), 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
9772
  "a",
9781
9773
  {
9782
9774
  key: lIdx,
@@ -9793,10 +9785,10 @@ function QuestTimeline({
9793
9785
  key: iIdx,
9794
9786
  type: "button",
9795
9787
  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"
9788
+ className: "relative w-12 h-12 rounded border border-white/10 overflow-hidden hover:border-sky-500/50 transition-colors group/img"
9797
9789
  },
9798
9790
  /* @__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" }))
9791
+ /* @__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
9792
  )))));
9801
9793
  })));
9802
9794
  }
@@ -9862,6 +9854,8 @@ function QuestDetailsPanel({
9862
9854
  const [noteLinks, setNoteLinks] = useState19([]);
9863
9855
  const [noteImages, setNoteImages] = useState19([]);
9864
9856
  const [isNoteFormOpen, setIsNoteFormOpen] = useState19(false);
9857
+ const [editingEntryId, setEditingEntryId] = useState19(null);
9858
+ const [editingEntryLabel, setEditingEntryLabel] = useState19("");
9865
9859
  const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
9866
9860
  const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
9867
9861
  initialWidth: isReadMode ? 700 : 440,
@@ -9952,12 +9946,11 @@ function QuestDetailsPanel({
9952
9946
  const actorName = (currentUser == null ? void 0 : currentUser.name) || "Revisor";
9953
9947
  const targetMember = viewMembers.find((m) => String(m.id) === String(returnAssigneeId));
9954
9948
  const targetName = (targetMember == null ? void 0 : targetMember.name) || "executor";
9955
- const newEvent = {
9956
- type: "review_returned",
9957
- timestamp: Date.now(),
9949
+ const newEvent = createTimelineEntry(
9958
9950
  actorId,
9959
- description: `Review finalizada com retorno para opera\xE7\xE3o por ${actorName}. Novo respons\xE1vel: ${targetName}.`
9960
- };
9951
+ `Status "In Progress" - Respons\xE1vel: ${targetName}`,
9952
+ "status_changed"
9953
+ );
9961
9954
  const updatedTimeline = [...timeline, newEvent];
9962
9955
  const updatedStatus = "In Progress";
9963
9956
  const updatedColor = "#eab308";
@@ -9993,14 +9986,30 @@ function QuestDetailsPanel({
9993
9986
  }
9994
9987
  };
9995
9988
  const handleAddTimelineNote = async () => {
9996
- if (!noteText.trim() || isSaving) return;
9989
+ if (!noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving) return;
9997
9990
  setIsSaving(true);
9998
9991
  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];
9992
+ let updatedTimeline;
9993
+ if (editingEntryId) {
9994
+ updatedTimeline = timeline.map((entry) => {
9995
+ if (entry.id === editingEntryId) {
9996
+ return {
9997
+ ...entry,
9998
+ notes: noteText.trim() ? entry.notes ? `${entry.notes}
9999
+ ${noteText}` : noteText : entry.notes,
10000
+ links: [...entry.links || [], ...noteLinks],
10001
+ images: [...entry.images || [], ...noteImages]
10002
+ };
10003
+ }
10004
+ return entry;
10005
+ });
10006
+ } else {
10007
+ const newEntry = createTimelineEntry(actorId, noteText, "comment", {
10008
+ links: noteLinks,
10009
+ images: noteImages
10010
+ });
10011
+ updatedTimeline = [...timeline, newEntry];
10012
+ }
10004
10013
  try {
10005
10014
  const nodeToSave = {
10006
10015
  ...node,
@@ -10013,12 +10022,26 @@ function QuestDetailsPanel({
10013
10022
  setNoteLinks([]);
10014
10023
  setNoteImages([]);
10015
10024
  setIsNoteFormOpen(false);
10025
+ setEditingEntryId(null);
10026
+ setEditingEntryLabel("");
10016
10027
  } catch (error) {
10017
10028
  console.error("Erro ao adicionar nota:", error);
10018
10029
  } finally {
10019
10030
  setIsSaving(false);
10020
10031
  }
10021
10032
  };
10033
+ const handleOpenNoteForEntry = (entry) => {
10034
+ setEditingEntryId(entry.id);
10035
+ setEditingEntryLabel(entry.description);
10036
+ setNoteText("");
10037
+ setNoteLinks([]);
10038
+ setNoteImages([]);
10039
+ setIsNoteFormOpen(true);
10040
+ setTimeout(() => {
10041
+ const form = document.getElementById("quest-note-form");
10042
+ form == null ? void 0 : form.scrollIntoView({ behavior: "smooth", block: "center" });
10043
+ }, 100);
10044
+ };
10022
10045
  const addLinkToNote = () => {
10023
10046
  setNoteLinks([...noteLinks, { id: v4_default(), name: "", value: "" }]);
10024
10047
  };
@@ -10040,8 +10063,18 @@ function QuestDetailsPanel({
10040
10063
  const handleStatusChange = (newStatus) => {
10041
10064
  setStatus(newStatus);
10042
10065
  const newColor = QUEST_STATUS_COLORS3[newStatus];
10066
+ const actorId = (currentUser == null ? void 0 : currentUser.id) || "unknown";
10067
+ const currentAssignee = viewMembers.find((m) => String(m.id) === String(assigneeId));
10068
+ const assigneeName = (currentAssignee == null ? void 0 : currentAssignee.name) || (currentAssignee == null ? void 0 : currentAssignee.email) || "Nenhum";
10069
+ const newEvent = createTimelineEntry(
10070
+ actorId,
10071
+ `Status "${newStatus}" - Respons\xE1vel: ${assigneeName}`,
10072
+ "status_changed"
10073
+ );
10074
+ const newTimeline = [...timeline, newEvent];
10075
+ setTimeline(newTimeline);
10043
10076
  onColorChange == null ? void 0 : onColorChange(node.id, newColor);
10044
- onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
10077
+ onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor, timeline: newTimeline });
10045
10078
  setHasUnsavedChanges(true);
10046
10079
  };
10047
10080
  const handleAddType = (newType) => {
@@ -10262,13 +10295,11 @@ function QuestDetailsPanel({
10262
10295
  {
10263
10296
  onClick: () => {
10264
10297
  setAssigneeId("");
10265
- const newTimeline = [...timeline, {
10266
- type: "assignee_changed",
10267
- timestamp: Date.now(),
10268
- actorId: "user",
10269
- to_assignee_id: null,
10270
- description: `Respons\xE1vel removido.`
10271
- }];
10298
+ const newTimeline = [...timeline, createTimelineEntry(
10299
+ (currentUser == null ? void 0 : currentUser.id) || "unknown",
10300
+ `Status "${status}" - Respons\xE1vel: Nenhum`,
10301
+ "status_changed"
10302
+ )];
10272
10303
  setTimeline(newTimeline);
10273
10304
  setHasUnsavedChanges(true);
10274
10305
  setIsAssigneeDropdownOpen(false);
@@ -10296,13 +10327,11 @@ function QuestDetailsPanel({
10296
10327
  key: member.id,
10297
10328
  onClick: () => {
10298
10329
  setAssigneeId(member.id);
10299
- const newTimeline = [...timeline, {
10300
- type: "assignee_changed",
10301
- timestamp: Date.now(),
10302
- actorId: "user",
10303
- to_assignee_id: member.id,
10304
- description: `Quest atribu\xEDda para ${member.name || member.email}.`
10305
- }];
10330
+ const newTimeline = [...timeline, createTimelineEntry(
10331
+ (currentUser == null ? void 0 : currentUser.id) || "unknown",
10332
+ `Status "${status}" - Respons\xE1vel: ${member.name || member.email}`,
10333
+ "status_changed"
10334
+ )];
10306
10335
  setTimeline(newTimeline);
10307
10336
  setHasUnsavedChanges(true);
10308
10337
  setIsAssigneeDropdownOpen(false);
@@ -10380,17 +10409,32 @@ function QuestDetailsPanel({
10380
10409
  "button",
10381
10410
  {
10382
10411
  type: "button",
10383
- onClick: () => setIsNoteFormOpen(!isNoteFormOpen),
10412
+ onClick: () => {
10413
+ if (isNoteFormOpen && editingEntryId) {
10414
+ setEditingEntryId(null);
10415
+ setEditingEntryLabel("");
10416
+ setNoteText("");
10417
+ setNoteLinks([]);
10418
+ setNoteImages([]);
10419
+ } else {
10420
+ setIsNoteFormOpen(!isNoteFormOpen);
10421
+ setEditingEntryId(null);
10422
+ setEditingEntryLabel("");
10423
+ }
10424
+ },
10384
10425
  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
10426
  },
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(
10427
+ isNoteFormOpen && !editingEntryId ? /* @__PURE__ */ React19.createElement(FiX7, { size: 12 }) : /* @__PURE__ */ React19.createElement(FiPlus8, { size: 12 }),
10428
+ isNoteFormOpen && !editingEntryId ? "Cancelar" : "Nova Nota"
10429
+ )), 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: () => {
10430
+ setIsNoteFormOpen(false);
10431
+ setEditingEntryId(null);
10432
+ }, className: "text-slate-500 hover:text-white" }, /* @__PURE__ */ React19.createElement(FiX7, { size: 14 }))), /* @__PURE__ */ React19.createElement(
10389
10433
  "textarea",
10390
10434
  {
10391
10435
  value: noteText,
10392
10436
  onChange: (e) => setNoteText(e.target.value),
10393
- placeholder: "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10437
+ placeholder: editingEntryId ? "Adicione uma observa\xE7\xE3o a esta etapa..." : "Escreva uma observa\xE7\xE3o no hist\xF3rico...",
10394
10438
  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
10439
  }
10396
10440
  ), /* @__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 +10469,18 @@ function QuestDetailsPanel({
10425
10469
  {
10426
10470
  type: "button",
10427
10471
  onClick: handleAddTimelineNote,
10428
- disabled: !noteText.trim() || isSaving,
10472
+ disabled: !noteText.trim() && noteLinks.length === 0 && noteImages.length === 0 || isSaving,
10429
10473
  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
10474
  },
10431
- "Adicionar ao Hist\xF3rico"
10475
+ editingEntryId ? "Salvar nesta Etapa" : "Adicionar ao Hist\xF3rico"
10432
10476
  ))), /* @__PURE__ */ React19.createElement(
10433
10477
  QuestTimeline,
10434
10478
  {
10435
10479
  timeline,
10436
10480
  viewMembers,
10437
- onOpenImageViewer
10481
+ onOpenImageViewer,
10482
+ canEdit,
10483
+ onAddNote: handleOpenNoteForEntry
10438
10484
  }
10439
10485
  )), /* @__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
10486
  const isSelected = size === s;
@@ -14003,7 +14049,7 @@ function XViewScene({
14003
14049
  } = questNode;
14004
14050
  const newTimeline = [...cleanQuestNode.timeline || [], createTimelineEntry(
14005
14051
  ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id) || "unknown",
14006
- `Quest iniciada ("In Progress") por ${((_b2 = session == null ? void 0 : session.user) == null ? void 0 : _b2.name) || "usu\xE1rio"}.`,
14052
+ `Status "In Progress" - Respons\xE1vel: ${((_b2 = session == null ? void 0 : session.user) == null ? void 0 : _b2.name) || "usu\xE1rio"}`,
14007
14053
  "status_changed"
14008
14054
  )];
14009
14055
  const updatedNode = {
@@ -14041,15 +14087,15 @@ function XViewScene({
14041
14087
  nextIdxToAssign = currentReviewerIdx + 1;
14042
14088
  newTimeline.push(createTimelineEntry(
14043
14089
  ((_a2 = session == null ? void 0 : session.user) == null ? void 0 : _a2.id) || cleanQuestNode.assignee_id,
14044
- `Review conclu\xEDda por ${((_b2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _b2.name) || "revisor"}.`,
14045
- "review_completed"
14090
+ `Status "Review" - Conclu\xEDda por ${((_b2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _b2.name) || "revisor"}`,
14091
+ "status_changed"
14046
14092
  ));
14047
14093
  } else {
14048
14094
  nextIdxToAssign = currentReviewerIdx !== -1 ? currentReviewerIdx : reviewStack.length;
14049
14095
  newTimeline.push(createTimelineEntry(
14050
14096
  ((_c2 = session == null ? void 0 : session.user) == null ? void 0 : _c2.id) || cleanQuestNode.assignee_id,
14051
- `Tarefa principal entregue por ${((_d2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _d2.name) || ((_e2 = session == null ? void 0 : session.user) == null ? void 0 : _e2.name) || "respons\xE1vel"}.`,
14052
- "task_completed"
14097
+ `Status "Done" - Finalizado por ${((_d2 = viewMembers.find((m) => m.id === cleanQuestNode.assignee_id)) == null ? void 0 : _d2.name) || ((_e2 = session == null ? void 0 : session.user) == null ? void 0 : _e2.name) || "respons\xE1vel"}`,
14098
+ "status_changed"
14053
14099
  ));
14054
14100
  }
14055
14101
  if (nextIdxToAssign < reviewStack.length) {
@@ -14061,8 +14107,8 @@ function XViewScene({
14061
14107
  nextStatus = "Review";
14062
14108
  newTimeline.push(createTimelineEntry(
14063
14109
  "system",
14064
- `Quest atribu\xEDda para revis\xE3o a ${((_f2 = viewMembers.find((m) => m.id === nextAssigneeId)) == null ? void 0 : _f2.name) || "revisor"}.`,
14065
- "assignee_changed",
14110
+ `Status "Review" - Respons\xE1vel: ${((_f2 = viewMembers.find((m) => m.id === nextAssigneeId)) == null ? void 0 : _f2.name) || "revisor"}`,
14111
+ "status_changed",
14066
14112
  { metadata: { to_assignee_id: nextAssigneeId } }
14067
14113
  ));
14068
14114
  } else {
@@ -14077,8 +14123,8 @@ function XViewScene({
14077
14123
  if (nextStatus === "Done") {
14078
14124
  newTimeline.push(createTimelineEntry(
14079
14125
  "system",
14080
- `Quest finalizada!`,
14081
- "quest_finished"
14126
+ `Status "Done" - Quest Finalizada`,
14127
+ "status_changed"
14082
14128
  ));
14083
14129
  }
14084
14130
  const updatedNode = {
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.34",
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",