Flow delete offers to delete its runs, history included
DELETE /runs?flow= removes every Run row of the flow but left its flow_run rows, so a deleted flow kept appearing in the history. history=true now drops those too, and the flow delete dialogs (editor and bulk) send it when "Also delete its runs" is ticked.
This commit is contained in:
@@ -277,6 +277,7 @@ export function ConfirmDelete({
|
||||
pending,
|
||||
description,
|
||||
onConfirm,
|
||||
children,
|
||||
}: {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
@@ -286,6 +287,8 @@ export function ConfirmDelete({
|
||||
/** What is actually lost, when the git-backed answer below is not it. */
|
||||
description?: string
|
||||
onConfirm: () => void
|
||||
/** An option the delete takes, drawn between the question and the buttons. */
|
||||
children?: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
@@ -307,6 +310,7 @@ export function ConfirmDelete({
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{children}
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Keep {names.length === 1 ? "it" : "them"}
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
FlowsService,
|
||||
type MessageSpec,
|
||||
type NodeDef_Input,
|
||||
RunsService,
|
||||
} from "@/client"
|
||||
import { DiscardDraftDialog } from "@/components/Common/Dock"
|
||||
import { useTheme } from "@/components/theme-provider"
|
||||
@@ -790,7 +791,13 @@ function FlowEditorInner({
|
||||
})
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: () => FlowsService.deleteFlow({ name: flowName }),
|
||||
mutationFn: async (withRuns: boolean) => {
|
||||
await FlowsService.deleteFlow({ name: flowName })
|
||||
// After the flow, which is refused while a run is live, so a refusal
|
||||
// leaves both standing.
|
||||
if (withRuns)
|
||||
await RunsService.deleteRuns({ flow: flowName, history: true })
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: flowKeys.all })
|
||||
setFlowPanelOpen(false)
|
||||
@@ -1327,7 +1334,7 @@ function FlowEditorInner({
|
||||
nodeCount={definitions.length}
|
||||
// Confirmed, so it is a finished edit rather than a run of keystrokes.
|
||||
onChange={(next) => commitDoc({ ...next, nodes: definitions }, true)}
|
||||
onDelete={() => deleteMutation.mutate()}
|
||||
onDelete={(withRuns) => deleteMutation.mutate(withRuns)}
|
||||
enabled={detail.enabled ?? true}
|
||||
toggling={enableMutation.isPending}
|
||||
onToggleEnabled={(next) => enableMutation.mutate(next)}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useState } from "react"
|
||||
import { useId, useState } from "react"
|
||||
|
||||
import type { FlowDef_Input } from "@/client"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -34,7 +35,7 @@ export function FlowPanel({
|
||||
definition: FlowDef_Input
|
||||
nodeCount: number
|
||||
onChange: (next: FlowDef_Input) => void
|
||||
onDelete: () => void
|
||||
onDelete: (withRuns: boolean) => void
|
||||
hasDraft: boolean
|
||||
enabled: boolean
|
||||
toggling: boolean
|
||||
@@ -42,6 +43,7 @@ export function FlowPanel({
|
||||
onClose: () => void
|
||||
}) {
|
||||
const [confirmOpen, setConfirmOpen] = useState(false)
|
||||
const [withRuns, setWithRuns] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -64,7 +66,10 @@ export function FlowPanel({
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive"
|
||||
onClick={() => setConfirmOpen(true)}
|
||||
onClick={() => {
|
||||
setWithRuns(false)
|
||||
setConfirmOpen(true)
|
||||
}}
|
||||
data-testid="delete-flow"
|
||||
>
|
||||
Delete flow
|
||||
@@ -142,6 +147,7 @@ export function FlowPanel({
|
||||
stays in the flow store's git repository.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<AlsoDeleteRuns checked={withRuns} onCheckedChange={setWithRuns} />
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setConfirmOpen(false)}>
|
||||
Keep it
|
||||
@@ -150,7 +156,7 @@ export function FlowPanel({
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
setConfirmOpen(false)
|
||||
onDelete()
|
||||
onDelete(withRuns)
|
||||
}}
|
||||
data-testid="confirm-delete-flow"
|
||||
>
|
||||
@@ -162,3 +168,35 @@ export function FlowPanel({
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether deleting a flow takes its runs along. Off by default: a run is a
|
||||
* record of what ran, and keeping one keeps the artifacts it recorded.
|
||||
*/
|
||||
export function AlsoDeleteRuns({
|
||||
checked,
|
||||
onCheckedChange,
|
||||
plural = false,
|
||||
}: {
|
||||
checked: boolean
|
||||
onCheckedChange: (checked: boolean) => void
|
||||
plural?: boolean
|
||||
}) {
|
||||
const id = useId()
|
||||
return (
|
||||
<div className="grid gap-1">
|
||||
<label htmlFor={id} className="flex items-center gap-2 text-sm">
|
||||
<Checkbox
|
||||
id={id}
|
||||
checked={checked}
|
||||
onCheckedChange={(next) => onCheckedChange(next === true)}
|
||||
data-testid="delete-flow-runs"
|
||||
/>
|
||||
Also delete {plural ? "their" : "its"} runs
|
||||
</label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Otherwise the runs stay, and keep their artifacts on disk.
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user