diff --git a/packages/ui/src/features/loops/components/LoopDetailView.tsx b/packages/ui/src/features/loops/components/LoopDetailView.tsx index f16b8b85bf..8318a684cf 100644 --- a/packages/ui/src/features/loops/components/LoopDetailView.tsx +++ b/packages/ui/src/features/loops/components/LoopDetailView.tsx @@ -1,16 +1,26 @@ import { ArrowLeftIcon, RepeatIcon } from "@phosphor-icons/react"; import type { LoopSchemas } from "@posthog/api-client/loops"; -import { Switch } from "@posthog/quill"; +import { + AlertDialog, + AlertDialogClose, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + Badge, + Button, + Switch, + Textarea, +} from "@posthog/quill"; import { useSetHeaderContent } from "@posthog/ui/hooks/useSetHeaderContent"; -import { Badge } from "@posthog/ui/primitives/Badge"; -import { Button } from "@posthog/ui/primitives/Button"; import { toast } from "@posthog/ui/primitives/toast"; import { navigateToEditLoop, navigateToLoops, } from "@posthog/ui/router/navigationBridge"; -import { AlertDialog, Flex, Text } from "@radix-ui/themes"; -import { useState } from "react"; +import { Flex, Text } from "@radix-ui/themes"; +import { useRef, useState } from "react"; import { useLoop } from "../hooks/useLoop"; import { useDeleteLoop, @@ -101,24 +111,25 @@ export function LoopDetailView({ loopId }: { loopId: string }) {
- + {loop.name} - + {loopStatusLabel(loop)} - {loop.visibility} + {loop.visibility} - - - - - - - + + + + Delete loop + + + Permanently delete{" "} + {loop.name}? + This stops every trigger and cannot be undone. + + + + + + Cancel + + } + /> + + + +
); } +function loopStatusBadgeVariant( + loop: LoopSchemas.Loop, +): "default" | "destructive" | "success" { + const color = loopStatusColor(loop); + if (color === "green") return "success"; + if (color === "red") return "destructive"; + return "default"; +} + function ConfigSummarySection({ loop }: { loop: LoopSchemas.Loop }) { return ( @@ -278,13 +298,74 @@ function ConfigSummarySection({ loop }: { loop: LoopSchemas.Loop }) { )} + + + ); +} - -
-            {loop.instructions}
-          
-
+function InstructionsSection({ loop }: { loop: LoopSchemas.Loop }) { + const updateLoop = useUpdateLoop(loop.id); + const [draft, setDraft] = useState(null); + // Escape reverts and blurs; skip the resulting onBlur save. + const skipCommit = useRef(false); + + const commit = (value: string) => { + if (skipCommit.current) { + skipCommit.current = false; + return; + } + const trimmed = value.trim(); + if (!trimmed) { + setDraft(null); + return; + } + if (updateLoop.isPending) return; + if (trimmed === loop.instructions.trim()) { + setDraft(null); + return; + } + updateLoop.mutate( + { instructions: trimmed }, + { + onSuccess: () => { + setDraft(null); + toast.success("Instructions updated"); + }, + onError: (error) => { + setDraft(null); + toast.error("Failed to update instructions", { + description: error.message, + }); + }, + }, + ); + }; + + return ( + + + + Instructions + + {updateLoop.isPending ? ( + Saving… + ) : null} +