diff --git a/src/routes/v2/pages/Editor/components/UpgradeComponents/components/UpgradeCandidateDetail.tsx b/src/routes/v2/pages/Editor/components/UpgradeComponents/components/UpgradeCandidateDetail.tsx index a4618e4da..42c1211ab 100644 --- a/src/routes/v2/pages/Editor/components/UpgradeComponents/components/UpgradeCandidateDetail.tsx +++ b/src/routes/v2/pages/Editor/components/UpgradeComponents/components/UpgradeCandidateDetail.tsx @@ -28,7 +28,11 @@ function EmptyDetail() { ); } -function PredictedIssuesSection({ issues }: { issues: ValidationIssue[] }) { +export function PredictedIssuesSection({ + issues, +}: { + issues: ValidationIssue[]; +}) { if (issues.length === 0) return null; return ( diff --git a/src/routes/v2/pages/Editor/nodes/TaskNode/context/TaskDetails/TaskDetails.tsx b/src/routes/v2/pages/Editor/nodes/TaskNode/context/TaskDetails/TaskDetails.tsx index 3e3133acb..fc3214cb9 100644 --- a/src/routes/v2/pages/Editor/nodes/TaskNode/context/TaskDetails/TaskDetails.tsx +++ b/src/routes/v2/pages/Editor/nodes/TaskNode/context/TaskDetails/TaskDetails.tsx @@ -16,6 +16,8 @@ import { Heading, Text } from "@/components/ui/typography"; import useToastNotification from "@/hooks/useToastNotification"; import { useAnalytics } from "@/providers/AnalyticsProvider"; import { AnnotationsBlock } from "@/routes/v2/pages/Editor/components/AnnotationsBlock/AnnotationsBlock"; +import { PredictedIssuesSection } from "@/routes/v2/pages/Editor/components/UpgradeComponents/components/UpgradeCandidateDetail"; +import { buildUpgradeCandidateFromResolved } from "@/routes/v2/pages/Editor/components/UpgradeComponents/utils/buildUpgradeCandidateFromResolved"; import { useTaskActions } from "@/routes/v2/pages/Editor/store/actions/useTaskActions"; import { useEditorSession } from "@/routes/v2/pages/Editor/store/EditorSessionContext"; import { useSpec } from "@/routes/v2/shared/providers/SpecContext"; @@ -26,7 +28,7 @@ import { ZINDEX_ANNOTATION, } from "@/utils/annotations"; import type { HydratedComponentReference } from "@/utils/componentSpec"; -import { diffComponentIO } from "@/utils/componentSpecDiff"; +import { componentMetadata } from "@/utils/componentTracking"; import { DEFAULT_NODE_DIMENSIONS } from "@/utils/constants"; import { tracking } from "@/utils/tracking"; @@ -92,19 +94,29 @@ export const TaskDetails = observer(function TaskDetails({ hydratedComponent: HydratedComponentReference; onChoose: (action: "update" | "import" | "place") => void; }) => { - const { inputDiff, outputDiff } = diffComponentIO< - { name: string; type?: unknown }, - { name: string; type?: unknown } - >(task.resolvedComponentSpec, hydratedComponent.spec); + // Model the edit as an upgrade candidate so we reuse the upgrade flow's + // diff + predicted-issues computation in the choose-action view. + const candidate = buildUpgradeCandidateFromResolved( + task.$id, + task.name, + task.componentRef.digest ?? "", + task.resolvedComponentSpec, + hydratedComponent, + spec, + ); return ( + > + + ); }; @@ -112,6 +124,12 @@ export const TaskDetails = observer(function TaskDetails({ const result = replaceTask(spec, task.$id, hydratedComponent); const lostInputs = result.inputDiff?.lostEntities ?? []; + track("pipeline_editor.component.edited", { + ...componentMetadata(hydratedComponent, "user"), + action: "update", + lost_inputs_count: lostInputs.length, + }); + if (lostInputs.length > 0) { const inputNames = lostInputs.map((input) => input.name).join(", "); notify( @@ -153,6 +171,12 @@ export const TaskDetails = observer(function TaskDetails({ }); const newTask = addTask(spec, hydratedComponent, position); + + track("pipeline_editor.component.edited", { + ...componentMetadata(hydratedComponent, "user"), + action: "place", + }); + notify("Task added", "success"); // Reveal the new node: animate the viewport to it, then spotlight it.