(null);
+
+ useEffect(() => {
+ if (open) {
+ setState("input");
+ setComments("");
+ }
+ }, [open]);
+
+ useEffect(() => {
+ if (open && state === "input" && textareaRef.current) {
+ textareaRef.current.focus();
+ }
+ }, [open, state]);
+
+ const handleSubmit = async () => {
+ setState("submitting");
+ try {
+ await onSubmit(comments);
+ setState("success");
+ } catch {
+ setState("input");
+ }
+ };
+
+ const canClose = state !== "submitting";
+
+ return (
+
+ );
+}
diff --git a/src/config/remoteTroubleshootAction.ts b/src/config/remoteTroubleshootAction.ts
new file mode 100644
index 000000000..c8dad2bd9
--- /dev/null
+++ b/src/config/remoteTroubleshootAction.ts
@@ -0,0 +1,15 @@
+export interface RemoteTroubleshootActionConfig {
+ endpointUrl: string;
+ buttonText: string;
+ modalTitle?: string;
+ modalDescription?: string;
+ successTitle?: string;
+ successMessage?: string;
+ source?: string;
+}
+
+declare global {
+ interface Window {
+ __TANGLE_REMOTE_TROUBLESHOOT_ACTION__?: RemoteTroubleshootActionConfig;
+ }
+}
diff --git a/src/routes/v2/pages/RunView/nodes/TaskNode/context/RunViewTaskDetails.tsx b/src/routes/v2/pages/RunView/nodes/TaskNode/context/RunViewTaskDetails.tsx
index 4f64a9671..fb96c42f6 100644
--- a/src/routes/v2/pages/RunView/nodes/TaskNode/context/RunViewTaskDetails.tsx
+++ b/src/routes/v2/pages/RunView/nodes/TaskNode/context/RunViewTaskDetails.tsx
@@ -1,3 +1,4 @@
+import { useParams } from "@tanstack/react-router";
import { AmphoraIcon, InfoIcon, LogsIcon } from "lucide-react";
import { observer } from "mobx-react-lite";
@@ -7,6 +8,7 @@ import Logs, {
OpenLogsInNewWindowLink,
} from "@/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskOverview/logs";
import { LogsEventsOverlaySection } from "@/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskOverview/LogsEventsOverlaySection";
+import { RemoteTroubleshootButton } from "@/components/shared/RemoteTroubleshootAction/RemoteTroubleshootButton";
import { StatusIcon } from "@/components/shared/Status";
import TaskDetails from "@/components/shared/TaskDetails/Details";
import { Icon } from "@/components/ui/icon";
@@ -31,6 +33,9 @@ export const RunViewTaskDetails = observer(function RunViewTaskDetails({
const { track } = useAnalytics();
const spec = useSpec();
const executionData = useExecutionDataOptional();
+ const params = useParams({ strict: false });
+ const runId =
+ "id" in params && typeof params.id === "string" ? params.id : undefined;
const task = spec?.tasks.find((t) => t.$id === entityId);
@@ -69,6 +74,15 @@ export const RunViewTaskDetails = observer(function RunViewTaskDetails({
+ {runId && (
+
+ )}
+
();
+
+export function getRemoteTroubleshootRecord(
+ runId: string,
+ executionId: string,
+): RemoteTroubleshootRecord | undefined {
+ const records = storage.getItem("remote-troubleshoot-requests") ?? [];
+ return records.find(
+ (r) => r.runId === runId && r.executionId === executionId,
+ );
+}
+
+export function saveRemoteTroubleshootRecord(
+ runId: string,
+ executionId: string,
+): void {
+ const records = storage.getItem("remote-troubleshoot-requests") ?? [];
+ const exists = records.some(
+ (r) => r.runId === runId && r.executionId === executionId,
+ );
+ if (exists) return;
+ storage.setItem("remote-troubleshoot-requests", [
+ ...records,
+ { runId, executionId, requestedAt: new Date().toISOString() },
+ ]);
+}